当前位置: 首页 > article >正文

MySQL事物,MVCC机制

事物

事务(Transaction)是一组作为单个逻辑工作单元执行的SQL语句,它具有ACID特性:

  • 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败,不能部分成功或部分失败
  • 一致性(Consistency):事务执行前后,数据库的状态保持一致
  • 隔离性(Isolation):多个事务并发执行时,每个事务的执行结果不受其他事务的影响
  • 持久性(Durability):事务一旦提交,对数据库的修改将永久保存

事务隔离级别

并发控制时会出现问题

  • 脏读:一个事务读取另一个事务未提交的数据
  • 不可重复读:同一事务内多次读取数据时,数据发生变化
  • 幻读:事务之间的数据插入或删除导致查询结果不一致

MySQL的隔离级别可以解决上述问题,支持四种隔离级别

  1. 读未提交(Read Uncommitted)

    • 允许脏读、不可重复读和幻读
    • 事务可以读取其他事务未提交的数据
  2. 读已提交(Read Committed)

    • 解决了脏读问题,但仍然可能发生不可重复读和幻读
    • 事务只能读取其他事务已提交的数据
  3. 可重复读(Repeatable Read)

    • 解决了脏读和不可重复读问题,但仍然可能发生幻读
    • 事务会对读取的数据加锁,保证该数据在事务内不会被修改
  4. 串行化(Serializable)

    • 解决了脏读、不可重复读和幻读问题
    • 对同一行记录,写操作会加锁,读操作会加共享锁,从而避免幻读

在这里插入图片描述

-- 查看当前事务隔离级别
SHOW VARIABLES LIKE 'transaction_isolation';
-- 设置事务隔离级别
-- READ-UNCOMMITTED(最低级别)
-- READ-COMMITTED
-- REPEATABLE-READ(MySQL 的默认隔离级别)
-- SERIALIZABLE(最高级别)
SET GLOBAL transaction_isolation = 'REPEATABLE-READ';
-- 开启事务
START TRANSACTION;
-- 提交事务
COMMIT;

MVCC

在MySQL中,默认的隔离级别是可重复读,可以解决脏读和不可重复读的问题,但不能解决幻读问题。如果想要解决幻读问题,就需要采用串行化的方式,也就是将隔离级别提升到最高,但这样一来就会大幅降低数据库的事务并发能力。
主要解决了在高并发环境下的数据库读写冲突问题,它允许事务处理更加高效,同时提高了数据库的整体性能和可靠性

什么是快照读和当前读?
  1. 快照读读取的数据是快照数据,不加锁的简单的SELECT操作都属于快照读
  2. 当前读读取的数据是当前最新的数据,需要加锁的SELECT操作都属于当前读

快照读就是普通的读操作,而当前读包括了加锁的读取和DML操作

什么是 MVCC

MVCC(Multi-Version Concurrency Control,多版本并发控制),通过数据行的多个版本管理来实现数据库的并发控制,它的思想就是保存数据的历史版本
在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题

MVCC实现原理

MVCC 的实现主要依赖于:隐藏字段、undo log、Read View。
在内部实现中,InnoDB 通过数据行的 DB_TRX_ID 和 Read View 来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR 找到 undo log 中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建 Read View 之前已经提交的修改和该事务本身做的修改

原文

隐藏字段

每条行记录除了自定义之外,InnoDB 还维护了以下隐藏字段

CREATE TABLE users (
   id INT,
   name VARCHAR(50)
   -- 以下是InnoDB自动维护的隐藏字段
   -- DB_TRX_ID     -- 事务ID:记录最后一次修改该行的事务ID
   -- DB_ROLL_PTR   -- 回滚指针:指向上一个版本的记录
   -- DB_ROW_ID     -- 如果没有设置主键且该表没有唯一非空索引时,InnoDB 会使用该 id 来生成聚簇索引
);
undo log

undo log 是用于事物回滚;当读取记录时,该记录被其他事物占用,则需要通过undo log来找到之前的数据版本,实现非锁定读。undo log主要分为两类:

  • insert undo log:事务对insert操作的undo loginsert操作的记录只对事务本身可见,对其他事务不可见,事务提交后,该undo log可以直接删除
  • update undo log:事务对update操作的undo log,事务提交后,该undo log会被purge线程删除

不同事物或者相同事物对同一行记录的update或者delete操作,会使该记录行的undo log成为一条链表,链首就是最新的记录,链尾就是最早的旧记录

Read View

Read View 记录了当前事务能看到的版本,这些版本的数据基于事务开始时的快照。
Read View的结构,Read View主要是用来做可见行判断,主要字段有

  • m_low_limit_id:目前出现过的最大的事务 ID+1,即下一个将被分配的事务 ID。大于等于这个 ID 的数据版本均不可见
  • m_up_limit_id:活跃事务列表 m_ids 中最小的事务 ID,如果 m_ids 为空,则 m_up_limit_id 为 m_low_limit_id。小于这个 ID 的数据版本均可见
  • m_ids:Read View 创建时其他未提交的活跃事务 ID 列表。创建 Read View时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。m_ids 不包括当前事务自己和已提交的事务(正在内存中)
  • m_creator_trx_id:创建该 Read View 的事务 ID
RC 和 RR 隔离级别下 MVCC 的差异

在事务隔离级别 RC 和 RR (InnoDB 存储引擎的默认事务隔离级别)下,InnoDB 存储引擎使用 MVCC(非锁定一致性读),但它们生成 Read View 的时机却不同

  • 在 RC 隔离级别下,每次select查询会生成并获取最新的Read View(m_ids 列表)
  • 在 RR 隔离级别下,同一个事务中的第一个快照读才会创建Read View,之后的快照读获取的都是同一个Read View
MVCC 解决不可重复读问题

举个例子:

事物1事物2事物3
T1START TRANSACTION;
T2START TRANSACTION;START TRANSACTION;
T3update users set name=‘Job’ where id=1;
T4select * from users where id=1;select * from users where id=1;
T5update users set name=‘Tom’ where id=1;
T6select * from users where id=1;select * from users where id=1;
T7commit;update users set name=‘Bill’ where id=1;
T8select * from users where id=1;
T9update users set name=‘Andrew’ where id=1;
T10commit;select * from users where id=1;
T11commit;

在RR隔离级别下

  • T2 时刻
    在这里插入图片描述

  • T4 时刻
    在这里插入图片描述

  • T6 时刻

在这里插入图片描述

  • T8 时刻

在这里插入图片描述

  • T10 时刻

在这里插入图片描述

在RC隔离级别下

  • T2 时刻

在这里插入图片描述

  • T4 时刻

在这里插入图片描述

  • T6 时刻

在这里插入图片描述

  • T8 时刻

在这里插入图片描述

  • T10 时刻

在这里插入图片描述

// todo 按照 read view 分析 …

MVCC➕Next-key-Lock 防止幻读
  1. 执行普通 select,此时会以 MVCC 快照读的方式读取数据
    RR 隔离级别只会在事务开启后的第一次查询生成 Read View ,并使用至事务提交。所以在生成 Read View 之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”

  2. 执行 select…for update/lock in share mode、insert、update、delete 等当前读
    当前读读取的是最新的数据,并且会对读取到的数据加锁,防止其他事务修改或删除该数据。在执行 insert、update、delete 操作时,会以当前读的方式读取数据,并加锁,防止其它事务在查询范围内插入数据,从而实现防止幻读


http://www.kler.cn/a/522943.html

相关文章:

  • OpenCV:形态学操作总结
  • 58.界面参数传递给Command C#例子 WPF例子
  • 设计模式面试题
  • 【学习笔记】计算机网络(二)
  • 判断子序列
  • 智慧园区系统分类及其在提升企业管理效率中的创新应用探讨
  • 【搜索回溯算法篇】:多源BFS--从简单BFS到多点协同,探索搜索算法的进化
  • 挂载mount
  • 可扩展架构:如何打造一个善变的柔性系统?
  • LTV预估 | 多视角对比学习框架CMLTV
  • 四层网络模型
  • mybatis(112/134)
  • Windows 程序设计5:文件的删除、复制与重命名操作
  • JVM栈溢出线上环境排查
  • 基于Ubuntu交叉编译ZLMediaKit
  • PCB Editor层叠文件(Gerber文件输出-01)
  • 【自然语言处理(NLP)】机器翻译之数据处理(数据收集、数据清洗、数据分词、数据标注、数据划分)
  • 2025年美赛数学建模C题 奥运奖牌表的模型
  • 2025.1.21——八、[HarekazeCTF2019]Avatar Uploader 2(未完成) 代码审计|文件上传
  • 代码随想录算法训练营第三十八天-动态规划-完全背包-322. 零钱兑换
  • 思维练习题
  • 【Unity3D】实现2D小地图效果
  • 忘记宝塔的访问地址怎么找
  • 【教学类-89-02】20250128新年篇02——姓名藏头对联(星火讯飞+Python,五言对联,有横批)
  • 项目测试之MockMvc
  • 【数据结构与算法】九大排序算法实现详解