搞定 MySQL 事务隔离+MVCC是什么(底层原理)

2 阅读4分钟

事务隔离级别是怎么实现的?

目录

  • 一、ACID原则
  • 二、并发问题
  • 三、如何解决并发问题?—— 通过事务隔离级别
  • 四、核心实现:MVCC + Read View

一、ACID原则

1、Atomicity (原子性)

执行一个事务中的所有操作,要么全部完成,要么全部不完成。

2、Consistency(一致性)

事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。

3、Isolation(隔离性)

在多个并发事务同时对其数据进行读写和修改的时候,防止交叉执行而导致数据的不一致,会给每一个并发事务一个完整的数据空间不被干扰。

4、Durability(持久性)

事务处理结束后,对数据的修改就是永久的,系统故障也不会丢失。

二、并发问题

当两个事务尝试更新相同的数据并且没有上锁时,就会发生这种情清况:较晚提交的事务会覆盖较早事务做的更改。并发问题有以下几种:

1、 dirty reads(脏读)

事务A改完,事务B基于此执行后,即使事务A撤销更改,事务B还是读取的是修改后的数据这一过程。

解决方法:READ COMMITTED(读已提交)及以上的事务隔绝,只要事务不能读取未提交的数据。

2、 Non-repeating reads(不可重复读)

在 同一个事务内,多次读取同一行数据,但由于 其他事务对该数据进行了修改并提交,导致 每次读取的结果不一致。----同一事务内多次读取同一数据,结果因其他事务的修改而不同。

解决方法:REPEATABLE READ(可重复读)及以上的隔离级别,保证在事务内多次读取同一数据是一致的

3、Phantom Read( 幻读)

在同一个事务中,多次执行相同的查询语句,由于 其他事务插入或删除了符合查询条件的记录并提交,导致每次查询出的记录数不一样,就像发生了“幻觉”。

----其他事务插入了新的符合你查询条件的数据,导致你“以为”你处理了所有数据,但实际上并没有。

三、如何解决并发问题?—— 通过事务隔离级别

事务隔离级别有以下四种:

1、Read Uncommitted(读未提交)

2、READ COMMITTED(读已提交)

一个事务只能读到其他事务已提交的数据,解决了脏读,但仍有不可重复读和幻读

3、REPEATABLE READ(可重复读)

保证多次读取同一数据一致,解决了脏读和不可重复读;

MySQL 在此级别通过 MVCC + Next-Key Locking 也有效控制了幻读

4、SERIALIZABLE(串行化)

最高隔离级别,它可以强制事务串行执行,完全避免并发问题,但性能最差。

四、核心实现:MVCC + Read View

1、什么是 MVCC?

多版本并发控制(Multi-Version Concurrency Control)

  • 它的核心是:保存数据的多个历史版本,通过版本来判断哪些数据对当前事务可见,从而实现不加锁的并发读写。

  • 每条记录的隐藏字段:

    • trx_id:最后修改这条记录的事务 ID。
    • roll_pointer:指向 Undo Log 中的历史版本。

    通过undo log保存历史版本形成版本链。

    通过read view判断哪个版本是可见的。

2、什么是 Read View?

读视图,可以理解为一个 “快照”,它在事务执行时创建,里面记录了四个字段:

  • creator_trx_id:创建这个 Read View 的事务 ID。
  • m_ids:创建 Read View 时,所有活跃且未提交的事务 ID 列表。
  • min_trx_idm_ids 中最小的事务 ID。
  • max_trx_id:创建 Read View 时,数据库下一个要分配的事务 ID。

3、可见性判断规则

当一个事务要读取某条记录时,会用记录的 trx_id 和 Read View 进行比较:

  1. 如果 trx_id < min_trx_id:这条记录在 Read View 创建前就已提交,可见

  2. 如果 trx_id > max_trx_id:这条记录是在 Read View 创建后才生成的,不可见

  3. 如果min_trx_id ≤ trx_id ≤ max_trx_id:

    • 如果 trx_idm_ids 里:说明是活跃事务,不可见
    • 如果 trx_id 不在 m_ids 里:说明已提交,可见

4、不同隔离级别下 Read View 的创建时机

  • 读已提交(RC)每次查询时,都会重新创建一个 Read View。所以每次查询都能看到其他事务最新提交的数据。
  • 可重复读(RR)事务中第一次查询时,创建一个 Read View,整个事务期间都复用它。所以整个事务看到的数据都是一致的,解决了不可重复读。