mvcc介绍

前提:在介绍mvcc之前,先简单介绍一下mysql事务的相关问题,mvcc归根结底是用来解决事务并发问题的,当然这个解决不是全部解决,只是解决了其中的一部分问题!
mysql事务
一、事务的基本要素(ACID)
  1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
   2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
   3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
   4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

二、事务的并发问题
  1、脏读:事务A读取了事务B未提交的数据,然后B回滚操作,那么A读取到的数据是脏数据 ----解决的基础隔离级别:读已提交
  2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。----解决的基础隔离级别:可重复读
  3、幻读:是针对数据插入操作来说的,假设在事务A中按照某个条件先后两次查询数据库,两次查询结果的条数不同(事务B在事务A两次查询期间插入符合查询条件的新数据),这种现象称为幻读。—解决的基础隔离级别:串行化(针对一张表的操作,每一个session依次完成自己的操作:行锁 + 间隙锁)MySQL事务隔离级别和实现原理
  小结:不可重复读与幻读的区别可以通俗的理解为:前者是数据变了,后者是数据的行数变了。不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。

三、MySQL事务隔离级别

MVCC多版本并发控制

前提概要
什么是MVCC
什么是当前读和快照读?
当前读,快照读和MVCC的关系
MVCC能解决什么问题,好处是?
MVCC实现原理
隐式字段
undo日志
Read View(读视图)
例子
MVCC相关问题
RR是如何在RC级的基础上解决不可重复读的?
RC,RR级别下的InnoDB快照读有什么不同?
一、前提概要
什么是MVCC?
MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问。

什么是当前读和快照读?
在学习MVCC多版本并发控制之前,我们必须先了解一下,什么是MySQL InnoDB下的当前读和快照读?

当前读
像select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读,它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。

快照读
简单的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本

MVCC能解决什么问题,好处是?
数据库并发场景有三种,分别为:

读-读:不存在任何问题,也不需要并发控制
读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
写-写:有线程安全问题,可能会存在更新丢失问题
MVCC带来的好处是?
多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该session的数据库的快照。 所以MVCC可以为数据库解决以下问题

在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
二、MVCC的实现原理
它的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View 来实现的。
隐式字段
每行记录除了我们自定义的字段外,还有数据库隐式定义的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段

DB_TRX_ID
6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
DB_ROLL_PTR
7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
DB_ROW_ID
6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
实际还有一个删除flag隐藏字段, 既记录被删除并不代表真的删除,而是删除flag变了

如上图,DB_ROW_ID是数据库默认为该行记录生成的唯一隐式主键,DB_TRX_ID是当前操作该记录的事务ID,而DB_ROLL_PTR是一个回滚指针,用于配合undo日志,指向上一个旧版本

undo日志
undo log主要分为两种:

insert undo log
代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃
update undo log
事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除
对MVCC有帮助的实质是update undo log ,undo log实际上就是存在rollback segment中旧记录链,它的执行流程如下:

一、 比如有个事务在persion表插入了一条新记录,记录如下,name为Jerry, age为24岁,隐式主键是1,事务ID和回滚指针,我们假设为NULL。为演示,插入提交后,该undo log被删除


二、 现在来了一个事务1对该记录的name做出了修改,改为Tom

在事务1修改该行(记录)数据时,数据库会先对该行加排他锁
然后把该行数据拷贝到undo log中,作为旧记录,既在undo log中有当前行的拷贝副本
拷贝完毕后,修改该行name为Tom,并且修改隐藏字段的事务ID为当前事务1的ID, 我们假设从1开始,之后递增,回滚指针指向拷贝到undo log的副本记录,既表示我的上一个版本就是它
事务提交后,释放锁

三、 又来了个事务2修改person表的同一个记录,将age修改为30岁

在事务2修改该行数据时,数据库也先为该行加锁
然后把该行数据拷贝到undo log中,作为旧记录,发现该行记录已经有undo log了,那么最新的旧数据作为链表的表头,插在该行记录的undo log最前面
修改该行age为30岁,并且修改隐藏字段的事务ID为当前事务2的ID, 那就是2,回滚指针指向刚刚拷贝到undo log的副本记录
事务提交,释放锁

记录版本链

从上面,我们就可以看出,同一记录的多次修改,会导致该记录的undo log成为一条记录版本线性表,既链表,undo log的链首就是最新的旧记录,链尾就是最早的旧记录

Read View(读视图)
什么是Read View?(对照表?)

什么是Read View,Read View是session进行快照读操作的时候生产的读视图(Read View),在该session执行的快照读的那一刻,会生成数据库系统当前的一个快照。
Read View主要是用来做可见性判断的, 即当执行快照读的时候,对该记录创建一个Read View读视图,把它比作条件用来判断当前session能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。
Read View:执行查询时【所有】未提交的事务Id数组(数组里最小的id为min_id)和已创建的最大事务id(max_id:未提交、已提交)组成


mvcc遵循一个可见性算法,查询时候,需要用read-view和undo log 做对比得到结果
比较规则:
依次比较记录版本链中每一条记录,符合规则就返回该条数据,不符合根据回滚指针取链路中的下一条数据;
trx_id为记录版本链里的DB_TRX_ID
1、如果trx_id < min_id(落在绿色部分),表示这个版本是已经提交的事务生成的,这个数据肯定是可见的;
2、如果trx_id > max_id (落在红色部分),表示这个版本是由将来的启动的事务生成的,是肯定不可见的;
3、如果min_id <= trx_id <= max_id (落在黄色部分),那就包括两种情况
a、若trx_id在未提交的事务Id数组里,表示这个版本是由未提交的事务产生的,不可见,当前自己的事务是可见的;
b、若trx_id不在未提交的事务Id数组里,表示这个版本是已经提交的事务生成的,可见

对于删除的情况,可以认为是update的特殊情况,会将版本链上最新的数据复制一份,然后将其trx_id修改成删除的trx_id,同时在该条记录的头信息(record_header)里的(delete_flag)标记位上写上true,来表示当前记录已经被删除,在查询时按照上面的规则查到对应的记录,如果delete_flag标记位等于true,意味着该条记录已被删除,则不返回数据

例子
默认RR级别
https://www.processon.com/view/link/5eef4a2d6376891e81dc7d28

三、MVCC相关问题
RC,RR级别下的InnoDB快照读有什么不同?
正是Read View生成时机的不同,从而造成RC,RR级别下快照读的结果的不同

在RR级别下的某个session对某条记录的第一次快照读会创建一个Read View, 将当前系统活跃的其他事务记录起来,此后再快照读的时候,还是使用的是同一个Read View,换一句话说,只要当前session在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可见;
而在RC级别下的,每次快照读都会新生成一个Read View, 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因
总之在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;而在RR隔离级别下,则是同一个session中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。
可参考文档
mvcc https://www.jianshu.com/p/8845ddca3b23
bin log、redo log、undo log区别 https://www.cnblogs.com/wq-blogs/p/11867199.html
事务 https://blog.csdn.net/qq_38997379/article/details/89017287
详细解读mysql 的undo log https://blog.csdn.net/qq_39459385/article/details/84644005
详细分析MySQL事务日志(redo log和undo log)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值