Mysql-事务(隔离级别,事务底层原理,MVCC)

什么是事务?有哪些特性?

事务:事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败。
事务特性:

  • 原子性(Atomicity): 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性(Consistency):事务前后数据的完整性必须保持一致
  • 隔离性(Isolation):多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。隔离性由隔离级别保障。
  • 持久性(Durability): 一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

事务并发可能产生那些问题?

1 脏读:一个事务读到了另一个事务未提交的数据
2 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发事务中的多次查询结果不一致,数据中的值不一致。
3 虚读 /幻读:一个事务读到了另一个事务已经插入(insert)的数据。导致事务中多次查询的结果不一致,比如一个事务向表中插入了一条数据,这个时候另一个事务读取到了这条数据这就是幻读,实际上不应该读到这条数据。
4 丢失更新,举个例子比如事务T1,T2都读取了表中的某一行数据,事务T1对一个表的数据做了更新更为值A,事务T1提交以后,T2也对这个数据进行了修改改为B并提交,这个时候在事务T1里面查询这个数据得到的值是B,T1对数据的修改A被丢失了。
导致这个问题的根本原因就是并发问题,这两个事务可以同时对这个数据进行修改

事务的隔离级别

1 read uncommitted 读未提交【RU】,一个事务读到另一个事务没有提交的数据
存在:3个问题(脏读、不可重复读、幻读)。
2 read committed 读已提交【RC】,一个事务读到另一个事务已经提交的数据
存在:2个问题(不可重复读、幻读)。
解决:1个问题(脏读)
3 repeatable read:可重复读【RR】,在一个事务中读到的数据始终保持一致,无论另一个事务是否提交
解决:3个问题(脏读、不可重复读、幻读)msql默认的隔离级别
4 serializable 串行化,同时只能执行一个事务,相当于事务中的单线程
解决:3个问题(脏读、不可重复读、幻读)

事务的底层原理

前面我们提到了四个问题,我们来解决这些问题。

解决方案一:基于锁并发控制LBCC

1 解决丢失更新的问题
我们在事务T1读取这样行数据的时候就直接加读锁,这个时候T2想要来修改这个数据需要加写锁是不被允许的,就必须等待这个读锁释放,T2才能对对数据进行更新,锁释放的时候事务T1已经执行完成,保证了不会丢失事务T1的更新。
在这里插入图片描述
2 解决读已提交(Read Committed)
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。这可以通过“瞬间共享读锁”和“排他写锁”实现, 即事务需要对某些数据进行修改必须对这些数据加 X 锁,事务结束后才会释放X锁,读数据时需要加上 S 锁,当数据读取完成后立刻释放 S 锁,不用等到事务结束。

3 解决可重复读取(Repeatable Read)
禁止不可重复读取和脏读取,但是有时可能出现幻读数据。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
Mysql默认使用该隔离级别。这可以通过“共享读锁”和“排他写锁”实现,即事务需要对某些数据进行修改必须对这些数据加 X 锁,读数据时需要加上 S 锁,当数据读取完成并不立刻释放 S 锁,而是等到事务结束后再释放。

解决方案二:基于版本并发控制MVCC

MVCC全称叫多版本并发控制,是RDBMS常用的一种并发控制方法,用来对数据库数据进行并发访问,实现事务。核心思想是读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突非常重要,极大的增加了系统的并发性能,这也是为什么几乎所有的RDBMS,都支持MVCC的原因。MVCC 实现原理关键在于数据快照,不同的事务访问不同版本的数据快照,从而实现事务下对数据的隔离级别。虽然说具有多个版本的数据快照,但这并不意味着必须拷贝数据,保存多份数据文件(这样会浪费存储空间),InnoDB通过事务的Undo日志巧妙地实现了多版本的数据快照。
这里介绍一下UndoLog
InnoDB 引擎对一条记录进行操作(修改、删除、新增)时,要把回滚时需要的信息都记录到 undo log 里,比如:
1)在插入一条记录时,要把这条记录的主键值记下来,这样之后回滚时只需要把这个主键值对应的记录删掉
2)在删除一条记录时,要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中.
3)在更新一条记录时,要把被更新的列的旧值记下来,这样之后回滚时再把这些列更新为旧值
会产生两种日志:
1 insert Undo 日志:在Insert操作中产生的Undo日志
由于insert操作的记录只对本身可见,对于其它事务此记录是不可见的,所以insert undo log 可以在事务提交后直接删除,不需要等待回收
在这里插入图片描述
2 Update Undo日志:Update或Delete 操作中产生的Undo日志
Update操作会对已经存在的行记录产生影响,为了实现MVCC多版本并发控制机制,因此Update Undo日志不能在事务提交时就删除,而是在事务提交时将日志放入指定区域,等待 Purge 线程进行最后的删除操作。
在这里插入图片描述
这个时候比如我们需要对第二次更新做回滚操作,这个时候只需要顺着RollPointer指针回退一个版本。

有了上面的Undo Log 我们可以结合是ReadView来实现版本控制
ReadView
ReadView是张存储事务id的表,主要包含当前系统中有哪些活跃的读写事务,把它们的事务id放到一个
列表中。结合Undo日志的默认字段【事务trx_id】来控制那个版本的Undo日志可被其他事务看见。

在这里插入图片描述
m_ids:表示在生成ReadView时,当前系统中活跃的读写事务id列表
m_low_limit_id:事务id下限,表示当前系统中活跃的读写事务中最小的事务id,m_ids事务列表中的最小事务id
m_up_limit_id:事务id上限,表示生成ReadView时,系统中应该分配给下一个事务的id值
m_creator_trx_id:表示生成该ReadView的事务的事务id

  • ReadView怎么产生,什么时候生成?
    开启事务之后,在第一次查询(select)时,生成ReadView
    RC 和 RR 隔离级别的差异本质是因为MVCC中ReadView的生成时机不同
    MVCC 怎么保证事务的RC和RR隔离级别可以看这篇文章, 根据下面判断可见性的原理做了一个案例演示。
    MVCC + ReadView案例

  • 如何判断可见性?
    开启事务执行第一次查询时,首先生成ReadView,然后依据Undo日志和ReadView按照判断可见性,
    按照下边步骤判断记录的版本链的某个版本是否可见。

如果被访问版本的 trx_id 属性值,小于ReadView中的事务下限id,表明生成该版本的事务在生
成 ReadView 前已经提交,所以该版本可以被当前事务访问。
如果被访问版本的 trx_id 属性值,等于ReadView中的 m_creator_trx_id ,可以被访问。
如果被访问版本的 trx_id 属性值,大于等于ReadView中的事务上限id,在生成 ReadView 后才产生的数据,所以该版本不可以被当前事务访问。
如果被访问版本的 trx_id 属性值,在事务下限id和事务上限id之间,那就需要判断是不是在
m_ids 列表中。如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。

循环判断Undo log中的版本链某一的版本是否对当前事务可见,如果循环到最后一个版本也不可见的
话,那么就意味着该条记录对该事务不可见,查询结果就不包含该记录。

MVCC下的读操作

在MVCC并发控制中,读操作可以分成两类:快照读 (Snapshot Read)与当前读 (Current Read)
快照读:读取的是记录的可见版本 (有可能是历史版本),不用加锁。刚才案例中都是快照读。
当前读:读取的是记录的最新版本,并且当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录

  • 快照读
    简单的select操作,属于快照读,不加锁。
  • 当前读
    特殊的读操作,或者插入/更新/删除操作,属于当前读,需要加锁,需要读取最新的数据。
select * from table where ? lock in share mode; # 加读锁
select * from table where ? for update;# 加写锁
insert into table values ();# 加写锁
update table set ? where ?;# 加写锁
delete from table where ?;# 加写锁
<think>我们正在处理一个关于MySQL事务隔离级别及其底层实现原理问题。用户明确要求了解隔离级别以及底层的数据结构实现原理。根据提供的引用内容,我们可以整理出以下关键点:1.事务的ACID特性(引用[2]):-原子性(Atomicity)-一致性(Consistency)-隔离性(Isolation)-持久性(Durability)2.MySQL事务隔离级别(引用[2])以及可能引发的问题-脏读-不可重复读-幻读3.实现原理部分提到了MVCC(多版本并发控制)锁(引用[3]):-MVCC通过行记录中的隐式字段、undologreadview来实现-此外,索引(如B+树索引Hash索引)锁也是重要的底层机制(引用[5])。4.存储引擎方面,提到了InnoDB(引用[3])Memory引擎(引用[4]),但我们的重点应该是InnoDB,因为它是支持事务的。因此,回答应该包括:-四种隔离级别(读未提交、读已提交、可重复读、串行化)以及每个级别解决的问题-底层实现原理MVCC(多版本并发控制)锁机制(记录锁、间隙锁等)。-相关的数据结构:undolog、readview、行记录中的隐式字段(DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID)等。注意:用户的问题中提到了“底层数据结构”,所以我们需要详细描述MVCC中使用的数据结构。另外,根据引用[3],MVCC实现依赖于:-行记录中的三个隐式字段:DB_TRX_ID(最后修改该行的事务ID)、DB_ROLL_PTR(指向该行回滚段的指针,即指向undolog记录)、DB_ROW_ID(行ID,当没有主键时,InnoDB会自动生成)。-undolog:存储事务修改前的数据,用于回滚一致性读。-readview:在事务进行快照读时产生,用于判断当前事务能够看到哪个版本的数据。同时,锁机制也是实现隔离级别(尤其是串行化)的重要手段。在可重复读隔离级别下,InnoDB通过间隙锁(next-keylock)来防止幻读。我们按照以下结构组织回答:1.事务隔离级别概述2.不同隔离级别解决的问题3.底层实现原理-MVCC(多版本并发控制)-锁机制4.关键数据结构(行格式、undolog、readview)注意:回答中引用的内容要标注引用标识。最后,按照要求生成相关问题。</think>### MySQL事务隔离级别及其底层实现原理 #### 一、事务隔离级别概述 MySQL支持四种事务隔离级别(由低到高): 1. **读未提交(Read Uncommitted)**:事务可读取其他未提交事务的数据,可能导致脏读、不可重复读幻读。 2. **读已提交(Read Committed)**:事务只能读取其他已提交事务的数据(解决脏读),但可能出现不可重复读幻读[^2]。 3. **可重复读(Repeatable Read)**:InnoDB默认级别,确保同一事务多次读取相同数据结果一致(解决不可重复读),通过间隙锁防止幻读[^2]。 4. **串行化(Serializable)**:最高隔离级别,所有操作串行执行(解决所有问题),但性能最低。 #### 二、底层数据结构与实现原理 ##### 1. MVCC(多版本并发控制) InnoDB实现隔离级别的核心机制,基于以下组件: - **行记录隐式字段**: - `DB_TRX_ID`(6字节):最近修改该行的事务ID - `DB_ROLL_PTR`(7字节):指向undo log的回滚指针 - `DB_ROW_ID`(6字节):行唯一标识(当无主键时自动生成)[^3] - **Undo Log**: - 存储数据的历史版本链,通过`DB_ROLL_PTR`串联 - 提供事务回滚一致性读所需的历史数据[^3] - **Read View**: - 事务开启时创建的数据可见性快照 - 包含:活跃事务ID列表、最小事务ID、下一个事务ID - 通过比较`DB_TRX_ID`与Read View判断数据可见性[^3] ##### 2. 锁机制 - **记录锁(Record Lock)**:锁定单行记录 - **间隙锁(Gap Lock)**:锁定索引范围间的间隙(防止幻读) - **Next-Key Lock**:记录锁+间隙锁的组合(可重复读隔离级的核心)[^2] - **共享锁(S锁)/排他锁(X锁)**:读写互斥控制 ##### 3. 数据访问流程示例(可重复读隔离级) ```mermaid graph LR A[事务启动] --> B[创建Read View] B --> C[查询数据] C --> D{检查行记录DB_TRX_ID} D -- 小于最小活跃ID --> E[可见] D -- 大于下一个ID --> F[不可见需回溯] F --> G[通过undo log找历史版本] ``` #### 三、不同隔离级别实现差异 | 隔离级别 | MVCC使用 | 锁机制 | 解决的问题 | |----------------|-------------------|--------------------------|------------------------| | 读未提交 | ❌ 直接读最新数据 | 仅写操作加锁 | 无 | | 读已提交 | ✅ 每次读新创Read View | 记录锁 | 脏读 | | 可重复读 | ✅ 首次读创Read View | Next-Key Lock | 脏读、不可重复读、幻读 | | 串行化 | ❌ | 范围锁+严格读写互斥 | 所有并发问题 | > ⚠️ **注意**:InnoDB在可重复读级别通过Next-Key Lock已能避免幻读,而非完全依赖MVCC[^2]。 #### 四、关键数据结构关系 ```mermaid classDiagram class 行记录 { DB_ROW_ID DB_TRX_ID DB_ROLL_PTR --> Undo_Log } class Undo_Log { 历史数据版本链 回滚指针 } class Read_View { trx_ids[] min_trx_id next_trx_id } 事务 --> Read_View ``` #### 五、性能影响 - MVCC通过非锁定读提升并发性能(读不阻塞写) - 锁机制尤其是间隙锁会增加死锁概率 -隔离级别需更多Undo Log存储空间[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值