多版本并发控制

MVCC

1、多版本并发控制:Multiversion Concurrency Control

2、MVCC 通过数据行的多个版本管理,实现数据库的并发控制

3、使得在 InnoDB 事务隔离级别下,保证执行一致性读操作,即为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值,查询时不用等待另一个事务释放锁

4、MVCC 没有正式的标准,在不同 DBMS 中 MVCC 实现方式可能不同

5、MySQL 只有 InnoDB 支持 MVCC

6、作用

(1)读写互相不阻塞,提升事务并发处理能力

(2)降低死锁概率,采用乐观锁,读取数据时并不需要加锁,对于写操作,也只锁定必要的行

(3)解决快照读的问题,当查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果

 

快照读

1、一致性读,读取的是快照数据,属于乐观锁

2、不加锁的简单的 SELECT 都属于快照读,即不加锁的非阻塞读

3、快照读的实现基于 MVCC,在很多情况下,避免加锁操作,降低开销

4、基于 MVCC,则快照读读到的并不一定是数据的最新版本,有可能是之前的历史版本

5、前提:隔离级别不是串行级别,串行级别下的快照读会退化成当前读

 

当前读

1、属于悲观锁

2、读取的是记录的最新版本,而不是历史版本的数据,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁

3、加锁的 SELECT,或对数据进行增删改都会进行当前读

 

MVCC 实现依赖

1、隐藏字段(trx_id、roll_pointer)

(1)InnoDB 表的聚簇索引记录中,都包含两个必要的隐藏列

(2)trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务 id 赋值给 trx_id 隐藏列

(3)roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到 undo 日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息

2、Undo Log 版本链

(1)每次对记录进行 UPDATE 操作,都会记录一条 undo 日志,每条 undo 日志也都有一个 roll_pointer 属性

(2)INSERT 操作对应 undo 日志没有 roll_pointer,因为该记录并没有更早的版本

(3)所有的版本都会被 roll_pointer 属性连接成一个链表

(4)版本链的头节点就是当前记录最新的值

(5)每个版本中包含生成该版本时对应的事务 id

3、ReadView

 

ReadView

1、在 MVCC 机制中,多个事务对同一个行记录进行更新,会产生多个历史快照,并保存在 Undo Log

2、ReadView:事务在使用 MVCC 机制进行快照读操作时产生的读视图

3、当事务启动时,会生成数据库系统当前的一个快照,InnoDB 为每个事务构造一个数组,用来记录并维护系统当前活跃事务 ID(活跃指启动但还没提交)

4、不同隔离级别的事务

(1)READ UNCOMMITTED:由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本

(2)SERIALIZABLE:InnoDB 规定使用加锁的方式来访问记录

(3)READ COMMITTED、REPEATABLE READ:都必须保证读到已经提交了的事务修改过的记录,若另一个事务已经修改记录但尚未提交,是不能直接读取最新版本的记录的

5、ReadView 解决行的可见性问题:一个事务查询行记录时,需要读取哪个版本的行记录,需要判断版本链中的哪个版本是当前事务可见的

(1)creator_trx_id:创建该 Read View 的事务 ID,只有对表中的记录做改动时(执行 INSERT、DELETE、UPDATE),才会为事务分配事务 ID,否则在一个只读事务中的事务 ID 值都默认为 0

(2)trx_ids:表示在生成 ReadView 时,当前系统中活跃的读写事务的事务 id 列表

(3)up_limit_id:活跃事务中最小的事务 ID

(4)low_limit_id:表示生成 ReadView 时,系统中应该分配给下一个事务的 id 值,low_limit_id 是系统最大的事务 id 值,系统中的事务 id,不是正在活跃的事务 ID

(5)low_limit_id 并不是 trx_ids 中的最大值,事务 id 是递增分配的,如:现有 id 为 1、2、3 三个事务,一个新的读事务在生成 ReadView 时,trx_ids 包括 1、2、3,up_limit_id 值为 1,low_limit_id 值为 4

6、判断记录的某个版本是否可见

(1)如果被访问版本的 trx_id,与 ReadView 中的 creator_trx_id 值相同,表示当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问

(2)如果被访问版本的 trx_id,小于 ReadView 中的 up_limit_id 值,表明生成该版本的事务,在当前事务生成 ReadView 前已经提交,所以该版本可以被当前事务访问

(3)如果被访问版本的 trx_id,大于或等于 ReadView 中的 low_limit_id 值,表明生成该版本的事务,在当前事务生成 ReadView 后才开启,所以该版本不可以被当前事务访问

(4)如果被访问版本的 trx_id,在 ReadView 的 up_limit_id 和 low_limit_id 之间,需要判断 trx_id 是否在 trx_ids 列表中

(5)如果在,说明创建 ReadView 时,生成该版本的事务还是活跃的,该版本不可以被访问

(6)如果不在,说明创建 ReadView 时,生成该版本的事务已经被提交,该版本可以被访问

 

系统通过 MVCC 查找记录

1、流程

(1)首先获取事务自己的版本号,即事务 ID

(2)获取 ReadView

(3)查询得到的数据,然后与 ReadView 中的事务版本号进行比较

(4)如果不符合 ReadView 规则,就需要从 Undo Log 中获取历史快照

(5)最后返回符合规则的数据

2、如果某个版本的数据对当前事务不可见,则顺着版本链找到下一个版本的数据,继续以上步骤判断可见性,依此类推,直到版本链中的最后一个版本

3、如果最后一个版本也不可见的话,表示该条记录对该事务完全不可见,查询结果就不包含该记录

4、InnoDB 中,MVCC 通过 Undo Log + Read View 进行数据读取

(1)Undo Log 保存历史快照

(2)Read View 规则判断当前版本的数据是否可见

5、在隔离级别为读已提交时,一个事务中的每一次 SELECT 查询,都会重新获取一次 Read View

6、当隔离级别为可重复读时,可以解决幻读问题,因为一个事务只在第一次 SELECT 时,会获取一次 Read View,而后面所有的 SELECT 都会复用这个 Read View

posted @   半条咸鱼  阅读(212)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示