MySQL MVCC原理1

MySQL知识总结

《MySQL是怎样运行的》知识总结

单表访问方法

表的连接原理

优化 基于查询成本的优化

优化需要的统计数据

优化 基于规则的优化

Explain详解

InnoDB缓冲区

事务

redo日志

undo 日志

MVCC原理

MySQL 锁

21 事务隔离级别与MVCC

事务并发执行的问题

事务的并发执行会出现一致性问题,解决方法有让事务串行执行,这种方式会严重影响到系统吞吐量、资源利用率,还有可串行化执行,对访问相同数据的事务进行限制,多个事务对同一个数据进行读写写写情况的访问时,才会出现一致性问题。

一致性问题

  • 脏写:事务修改了另一个未提交事务中的数据
  • 脏读:事务t1修改了数据,t2访问了未提交t1修改的数据后,t1终止,t2访问的数据是不正确的
  • 不可重复读:t1读取数据后,t2修改了未提交事务读取的数据,t1再次读取的数据与第一次读取的数据不一致
  • 幻读:t1读取了符合查询条件的记录,t2写入了符合查询条件的数据,t1再次读取数据不一致。

提示

对MySQL来说,幻读强调的是事务读取了之前没有读取的记录,对于“事务多次读取的数据不一致”的情况,MySQL把它归类为脏读

4种隔离级别

  • 读未提交
  • 读已提交
  • 可重复读
  • 序列化

MySQL支持4种隔离级别,默认的隔离级别是可重复读,MySQL在可重复读的级别下,很大程度解决了幻读

设置隔离级别

如果没有没有指明globalsession关键字,则只对下一个事务有效,可以通过启动选项transaction-isolation来指定服务器启动时的隔离级别

set [global|session] transaction isolation level 级别;

# 级别
read uncommitted
read committed
repeatable read
serializable
# 查看事务隔离级别
show variables like 'transaction_isolation';

MVCC原理

对于使用InnoDB存储引擎的表,聚簇索引记录有2个必要的隐藏列——trx_idroll_pointer,每对记录进行1次改动,都会记录一条undo日志,roll_pointer这个属性可以将undo日志串成一个链表,这个链表称为版本链,链表的头节点是记录的最新值trx_id属性记录了修改记录的事务Id。

利用这个版本链,在并发事务中,可以控制事务访问记录的行为,这种机制就是MVCC(Multi-Version Concurrency Control)。

提示

insert undo日志只在事务回滚时有作用,事务提交后,这类的undo日志就没用了,它占用的Undo Log Segment会被系统回收,但记录的roll_pointer的值不会被清除。

undo日志中只会记录索引列、被更新列的信息,如果在undo日志中没有某列的数据,就表示这列的数据和上版本的数据相同。

Read View

为了控制事务对记录的可见性,InnoDB提出了Read View,它的结构是:

  1. m_ids:生成Read View时,系统活跃事务的Id列表
  2. min_trx_id:生成Read View时,活跃的读写事务中最小的事务Id
  3. max_trx_id:生成Read View时,下一个事务Id
  4. creator_id:生成Read View的事务的Id

读已提交可重复读需要保证读取的是已提交的数据,这2个隔离级别会使用到Read View,且它们生成Read View的时机不同:

  • 可重复读:第一次读取数据前
  • 读已提交:每次读取数据前

提示

只有事务对表的记录进行第一次改动时,才会为这个事务分配事务Id,否则事务Id为0

根据Read View判断记录可见性

根据版本中trx_id和Read View进行判断

  • trx_id = creator_id,该版本对当前事务可见
  • trx_id < min_trx_id,修改记录的事务已提交,该版本对当前事务可见
  • trx_id ≥ max_trx_id,修改记录的事务在生成Read View后才开启,该版本对当前事务不可见
  • min_trx_id < trx_id < max_trx_id
    • trx_id在m_ids中:修改记录的事务未提交,该版本对当前事务不可见
    • trx_id不在m_ids中:修改的事务已提交trx_id在m_ids中该版本对当前事务可见
  • 如果版本对事务不可见,那就根据roll_pointer找到上一个版本,继续判断

二级索引与MVCC

只有聚簇索引中才有trx_idroll_pointer,使用二级索引查询数据保证可见性的方式是:

  1. 二级索引页面中有属性page_max_trx_id记录了对页面进行改动的最大事务Id(事务对页面的记录进行改动时,如果事务的Id大于这个属性,则将属性设置为执行改动的事务Id),如果Read View的这个属性page_max_trx_id < min_trx_id,则页面中的所有记录对事务可见,否则执行2。
  2. 根据二级索引主键值进行回表操作,在聚簇索引中找到可见的第一个版本,在版本中判断索引列的值是否符合查询条件,如果符合则返回给客户端(还有其他查询条件需继续判断条件是否成立),否则跳过记录。

提示

MVCC是使用隔离级别可重复读读已提交的事务执行普通select操作时,对版本链的访问的过程。

在执行deleteupdate语句时,并不会立刻把记录从页面中删除,而是进行了delete mark操作,这是为了MVCC服务。

关于Purge

因为update undo日志需要为MVCC服务,所以在事务提交后,不可立刻删除。

undo页面有Undo Log Header部分,它有trx_undo_history_node属性,是一个名为History链表的节点,事务提交后,就会把事务生成的update undo日志插入到History链表头部

Rollback Segment Header的页面中有属性trx_rseg_historyHistory链表的基节点、trx_rseg_history_size链表占用的页面数量。

每个回滚段有一个History链表,一个事务在某个回滚段中写入的一组update undo日志,在事务提交后,就会被加入到History链表,系统有很多回滚段,意味着可能有很多的History链表。

为了支持MVCC,delete mark操作不会将记录真正删除,在undo日志中Undo Log Headertrx_undo_del_marks属性标记本组undo日志是否有delete mark操作而产生的undo日志。在合适的时候,就会对update undo日志 、delete mark产生的undo日志进行purge操作。

只要系统中最早产生的ReadView不再访问这些日志,就可以清理这些undo日志。只要保证生成的Read View的事务已经提交了,ReadView就肯定不需要访问该事务生成的undo日志(该事务改动的记录的最新版本均对该Read View可见)。

InnoDB做了2件事,事务提交时,会为这个事务生成事务no,表示事务提交的顺序。undo日志的Undo Log Headertrx_undo_trx_no属性,事务提交时,就会将事务no填入这个属性中。History链表是按照事务提交的顺序来排列各组undo日志的(即按照事务no排序的)。

Read View还有事务no属性,生成Read View时,这个值设置为当前系统最大事务no+1。InnoDB还把系统中所有Read View按照创建时间练成一个链表,执行purge操作时,会把系统最早的Read View取出来(不存在则新创建一个),然后从回滚段的history链表中取出事务no较小的各组undo日志,如果一组undo日志的事务no小于最早的Read View的事务no,就说明该组undo日志没用了,也就可以在History链表中移除,释放空间,如果该组undo日志有delete mark生成的undo日志,就需要彻底删除。

可重复读的隔离级别下,事务会一直复用最初的Read View,如果事务运行很久,那Read View会一直不释放,这就导致update undo日志、delete mark生成的undo日志越来越多,表空间对应的文件越来越大,记录的版本链也越来越长,影响系统的性能。

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:创作都市 设计师:CSDN官方博客 返回首页
评论

打赏作者

011eH

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值