Mysql 中的MVCC原理,undo日志的依赖

一、 MVCC 原理了解

 
原文点击:MVCC原理浅析

  • MVCC: Multi-Version Concurrency Control
    多版本并发控制:当mysql 开启事务操作时,或者数据库崩溃恢复,都会用到MVCC机制,而不只是单单靠行锁去实现。而是一起使用
     
    当使用锁(尤其排他锁-行锁)进行并发控制,开销是非常大的 ,而使用MVCC机制来做,能一定程度的代替行锁,就可以有效降低系统开销

InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,分别保存了这个行的创建时间,一个保存的是行的删除时间。这里存储的并不是实际的时间值,而是系统版本号(可以理解为事务的ID),没开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的ID.

话外内容:############开始###############

InnoDB的内核,会对所有row数据增加三个内部属性:

(1)DB_TRX_ID,6字节,记录每一行最近一次修改它的事务ID;

(2)DB_ROLL_PTR,7字节,记录指向回滚段undo日志的指针;

(3)DB_ROW_ID,6字节,单调递增的行ID;

话外内容:############结束###############

如:列子,假设版本好初始为1

create table tb(
id int auto_increment primary key,
name cahr(32));

创建几条数据:insert 操作:第一个事务ID为1

start transaction;
insert into tb(name) values('shenjian') ;
insert into tb(name) values('zhangshan');
insert into tb(name) values('lisi');
commit;
id name 创建时间(事务ID) 删除时间(事务ID)
1 shenjian 1 undefinded
2 zhangsan 1 undefinded
3 lisi 1 undefinded

对select 的影响:

  1. innodb 只会去查找版本好小于或等于(早于)当前事务ID的数据行,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的

  2. 行的删除版本要么未定义,要么大于当前事务版本号,这可以确保事务读取到的行,在事务开始之前未被删除.
    只有1、2同时满足的记录,才能返回作为查询结果.

例2:delete操作
InnoDB会为删除的每一行保存当前系统的版本号(事务的ID)作为删除标识.
看下面的具体例子分析:
第二个事务,ID为2;

start transaction;
select * from tb;  //(第一步操作:1)
select * from tb;  //(第二部操作:2)
commit; 

假设:在这个事务的的执行过程中到第一步操作:1 的时候。
又开启了一个新事务(ID 为3)向表中执行Insert 操作插入数据

start transaction;
insert into tb(name) values('wangwu') ;
commit;

此时的表中数据为:

id name 创建时间(事务ID) 删除时间(事务ID)
1 shenjian 1 undefinded
2 zhangsan 1 undefinded
3 lisi 1 undefinded
4 wangwu 3 undefinded

然后刚才事务2,中的第二步:2 开始执行,引文id=4的数据的创建时间(事务ID为3),执行当前事务的ID为2,而InnoDB只会查找事务ID小于等于当前事务ID的数据行,所以id=4的数据行并不会在执行事务2中的第二步:2 中被检索出来,在事务2中的两条select 语句检索出来的数据都只会下表:

id name 创建时间(事务ID) 删除时间(事务ID)
1 shenjian 1 undefinded
2 zhangsan 1 undefinded
3 lisi 1 undefinded

再假设:假设在执行事务ID为2的过程中,刚执行到了第一步:1,假设事务执行完事务3后,接着又执行了事务4;
事务4:

st art transaction;
delete  from tb where id=1;
commit;

此时数据库中的表为:

id name 创建时间(事务ID) 删除时间(事务ID)
1 shenjian 1 4
2 zhangsan 1 undefinded
3 lisi 1 undefinded
4 wangwu 3 undefinded

接着执行事务ID为2的第二步:2,根据select 检索条件可以知道,它会检索创建时间(创建事务的ID)小于当前事务ID的行和删除时间(删除事务的ID)大于当前事务的行,而id=4的行上面已经说过,而id=1的行由于删除时间(删除事务的ID)大于当前事务的ID,所以事务2的第二步:2 select * from tb 会把id=1的数据检索出来.所以,事务2中的两条select 语句检索出来的数据都如下:

id name 创建时间(事务ID) 删除时间(事务ID)
1 shenjian 1 4
2 zhangsan 1 undefinded
3 lisi 1 undefinded

update:
InnoDB执行UPDATE,实际上是新插入了一行记录,并保存其创建时间为当前事务的ID,同时保存当前事务ID到要UPDATE的行的删除时间.

再再假设:
假设在执行完事务2的第一步:1 后又执行,其它用户执行了事务3,4,这时,又有一个用户对这张表执行了UPDATE操作:

第5个事务:

start transaction;
update tb set name='xxx' where id=2;
commit;

按照update的更新原则:会生成新的一行,并在原来要修改的列的删除时间列上添加本事务ID,得到表如下:

id name 创建时间(事务ID) 删除时间(事务ID)
1 shenjian 1 4
2 zhangsan 1 5
3 lisi 1 undefinded
4 wangwu 3 undefinded
2 xxx 5 undefinded

继续执行事务2第二步:2,根据select 语句的检索条件(创建时间事务ID小于自己查询事务ID的,和删除时间事务ID大于自身的查询事务ID),得到下表:

id name 创建时间(事务ID) 删除时间(事务ID)
1 shenjian 1 4
2 zhangsan 1 5
3 lisi 1 undefinded

并得不到上上个表的ID=4的第四行(因为创建事务ID=3 > 查询事务ID=2) 和第五行Update 事务创建的ID=2 的更新数据(因为 创建他的Update 事务ID=5 > 查询事务ID=2)

猜你喜欢

转载自www.cnblogs.com/shiqi17/p/9788386.html