欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

mysql的MVCC原理

程序员文章站 2022-07-07 19:49:52
MVCC:Multiversion concurrency control,多版本并发控制,提供并发访问数据库时,对事务内读取的到的内存做处理,用来避免写操作堵塞读操作的并发问题.InnoDB中锁可以分成读锁跟写锁,读锁与写锁是互斥的,通过锁机制可以实现解决事务并发带来的脏读,不可重复读,幻读,但是加锁的话开销大,数据库本身除了安全性还需要有较好的性能,所以就用MVCC代替了读锁,减少了加锁解锁的开销,也就是通过select 查询直接走MVCC,客户以跟update等操作并发执行.MVCC在mysql中...

MVCC:Multiversion concurrency control,多版本并发控制,提供并发访问数据库时,对事务内读取的到的内存做处理,用来避免写操作堵塞读操作的并发问题.
InnoDB中锁可以分成读锁跟写锁,读锁与写锁是互斥的,通过锁机制可以实现解决事务并发带来的脏读,不可重复读,幻读,但是加锁的话开销大,数据库本身除了安全性还需要有较好的性能,所以就用MVCC代替了读锁,减少了加锁解锁的开销和线程等待,也就是通过select 查询直接走MVCC,使得能够与客户update等写操作并发执行,并且解决事务并发的脏读,不可重复度,幻读等问题.
MVCC在mysql中只应用在read committed 和 repeatable read 两个事务隔离级别中,serializable使用的读锁+写锁直接锁表,强制事务串行化,应用非常少,并发性很低.
mysql中不同的锁的概念比较多,MVCC中锁概念分快照读与当前读,快照读是指select不加锁通过MVCC来控制,当前读是指insert delete update,for update,lock inshare mode需要加排他锁
mysql MVCC的实现是靠以下几个方面合作实现的:

1

InnoDB中每一条记录有隐藏列(以下两个列主要用于update跟insert,还会有一个删除标记,用于delete,被标记的数据是删除的数据)

DB_TRX_ID     长度为6个字节,存入插入或者更新语句中的最后一个事务id
DB_ROLL_PTR   长度为7个字节,称之为:回滚指针,指向写入回滚段的undo log记录.
DB_ROW_ID     如果有主键的情况下不会生成这列数据

每次事务开启系统都会分配一个事务id,并且事务id是递增的.
如果一个事务开启后事务id为1,那么下一个事务开启事务id就是2

2 undo log

update以及delete时,并不是将数据直接更新或者直接删除,而是copy出一条要操作的记录,将另外一条数据更新,并且更新DB_TRX_ID为操作的事务ID,更新DB_ROLL_PTR为数据之前的DB_TRX_ID:
比如id=1 name=‘li’ 这一条记录,是事务id为1的事务插入的
这条数据是这样的

id name DB_TRX_ID DB_ROLL_PTR
1 li 1 null

事务id为2的事务进行了更新,把name改成了’zhao’
此时先把上面的数据copy到undo log中,然后把数据更新成下面这样

id name DB_TRX_ID DB_ROLL_PTR
1 zhao 2 1

第二条的DB_ROLL_PTR 为1 指向第一条的DB_TRX_ID,也就是形成了一个链式结构,可以根据DB_ROLL_PTR 找到记录更新的链路.
delete也是这样,只是会在记录中标记删除标记
undo log用以实现事务中的roll back,并且可以控制事务只能看到某些版本中的数据.

3 read view

read view保存有这些信息:
m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小
值。
max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
creator_trx_id:表示生成该ReadView的事务的事务id。

这些信息用来确保数据是否可见:
如果DB_TRX_ID 等于creator_trx_id:说明数据是当前事务的,所以可见
如果数据DB_TRX_ID 小于min_trx_id,数据数据已经提交,所以这行数据可见
如果数据DB_TRX_ID 大于等于max_trx_id说明开启当前事务之前,数据没有提交,所以不可见
如果数据DB_TRX_ID大于等于min_trx_id小于等于max_trx_id中,如果在m_ids之中,说明这条数据的事务并没有提交,所以不可见,这样就解决了脏读问题,如果,不在m_ids中,说明已经提交,所以数据可见.
有了以上概念,分别说下RC级别跟RR级别是如何运作的.
RC只解决了脏读问题 RC事务隔离级别下,同一个事务中,每一次select都会生成一个read view
现有数据id=1,name=‘li’,事务A第一次select时生成readview,如果事务B在事务Aselect时已经开启,那么事务B的事务id将会在事务A的m_ids中,所以数据不可见,所以RC事务隔离级别下,是解决了脏读问题,但是由于事务A每一次select都会生成readview,如果事务B在事务A第一select之后就提交事务,事务A在第二次select时生成的readview中,事务B不在m_ids中(因为事务B已提交),并且此时max_trx_id要大于事务B的事务id,所以,事务B提交的数据对于事务A是可见的,所以不可重复读是没有避免的.
RR事务隔离级别下,事务A只有第一次select时才生成read view,并且后面select也是公用第一次生成的read view,所以在事务A后开启的事务肯定是大于等于事务A中的max_trx_id,与事务A同时开启的事务,会始终在m_ids列表中,所以更新的数据不可见,也就是说,RR事务隔离级别下,只有当前事务更改的操作,或者在当前事务select之前就提交的数据才能被本次事务可见,所以RR事务隔离级别是解决了不可重复读与幻读问题.

本文地址:https://blog.csdn.net/qq_28603127/article/details/107105839