事务隔离级别是怎么实现的?
目录
- 一、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_id:m_ids中最小的事务 ID。max_trx_id:创建 Read View 时,数据库下一个要分配的事务 ID。
3、可见性判断规则
当一个事务要读取某条记录时,会用记录的 trx_id 和 Read View 进行比较:
-
如果
trx_id < min_trx_id:这条记录在 Read View 创建前就已提交,可见。 -
如果
trx_id > max_trx_id:这条记录是在 Read View 创建后才生成的,不可见。 -
如果
min_trx_id ≤ trx_id ≤ max_trx_id:- 如果
trx_id在m_ids里:说明是活跃事务,不可见。 - 如果
trx_id不在m_ids里:说明已提交,可见。
- 如果
4、不同隔离级别下 Read View 的创建时机
- 读已提交(RC) :每次查询时,都会重新创建一个 Read View。所以每次查询都能看到其他事务最新提交的数据。
- 可重复读(RR) :事务中第一次查询时,创建一个 Read View,整个事务期间都复用它。所以整个事务看到的数据都是一致的,解决了不可重复读。