mysql可重复读现象及原理分析html
MySQL事务隔离级别和MVCC (undo日志版本链,ReadView) MVCC文章勘误sql
可重复读的实现并发
Repeatable Read(可重复读):一个事务在执行过程当中能够看到其余事务已经提交的新插入的记录(读已经提交的,实际上是读早于本事务开始且已经提交的),可是不能看到其余事务对已有记录的更新(即晚于本事务开始的),而且,该事务不要求与其余事务是“可串行化”的。(没有问题)mvc
这句话的核心,是“可是不能看到其余事务对已有记录的更新”,那么RR隔离级别是怎么保证这一点的呢?post
事务ID是递增的。spa
使用MVCC(多版本并发控制)。InnoDB为每行记录添加了一个事务ID,每当修改数据时,将当事务ID写入。
在读取事务开始时,系统会给事务一个当前版本号(事务ID),事务会读取版本号<=当前版本号的数据,这时就算另外一个事务插入一个数据,并立马提交,新插入这条数据的版本号会比读取事务的版本号高,所以读取事务读的数据仍是不会变。.net
可是,当前事务想再写这行数据的话,仍是以数据库提交的数据为准。而且会加行锁,其余的事务要再来修改的话,就得等到当前事务结束。命令行
举例:
咱们先看看现象,再分析原理。个人mysql版本是5.5。
下面是一张表,只有一条数据,而且我开启了事物
此时,另外一个事物将record加1,所以我在开启一个命令行客户端,执行下面的命令:
成功加1以后,实际上,数据库中record确定是2。
而后回到以前的客户端,再查一次:
没毛病,record仍是1,果真是可重复读。有些人觉得mysql的可重复读是经过行锁实现的,
从上面能够知道,确定不是,若是是的话,第一次select * from test的时候,id=1的记录就会加行锁,我都加行锁了,我还没提交,另外的事物是怎么update成功的。
结论就是mysql使用的MVCC(多版本并发控制),MVCC详解能够看:https://blog.csdn.net/whoamiyang/article/details/51901888
咱们继续,我以前的第一个事物还没提交,不过提交以前,我也想加1;
加完以后我再查一下,额,record是3,好像很奇怪,但也不奇怪。
其实,update test set record=record+1 where id=1;这条语句中,在加1以前,他知道本身等于2,而后2+1=3。
也就是说,update时读取数据是最新版本的数据,而select是到当前事物版本为止的数据。当更新成功以后,当前版本即为最新版本,再次select,读取的是最新的数据。
在这里讨论下乐观锁的必要性。下面是乐观锁的实现,实现乐观锁,咱们通常会这么作
update test set record=record+1 where id=1 and record=1;
若是不用乐观锁,你用select读取到的值其实根本不许确。除非你开启悲观锁,像下面这样:
select * from test where id=1 for update;
这样能够读取到最新的内容,同时在你当前的事物提交以前,其余事物的update此条记录将会锁等待。
故事到此,尚未结束,此时咱们开启事物三,也作加1操做看会发生什么。
结果是,锁等待超时,也就是说(事物一)在更新完后,会加行锁,这个应该比较好理解。事物中,刚开始查询的时候是不会加行锁的,可是当有更新操做以后,会加行锁,直到事物提交。
事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的所有数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,之后就会发生操做第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉同样。
1.在快照读读状况下,mysql经过mvcc来避免幻读。
2.在当前读读状况下,mysql经过next-key来避免幻。
mvcc全称是multi version concurrent control(多版本并发控制)。mysql把每一个操做都定义成一个事务,每开启一个事务,系统的事务版本号自动递增。每行记录都有两个隐藏列:建立版本号和删除版本号
select:事务每次只能读到建立版本号小于等于这次系统版本号的记录,同时行的删除版本号不存在或者大于当前事务的版本号。
update:插入一条新记录,并把当前系统版本号做为行记录的版本号,同时保存当前系统版本号到原有的行做为删除版本号。
delete:把当前系统版本号做为行记录的删除版本号
insert:把当前系统版本号做为行记录的版本号
行锁+GAP间隙锁
快照读:简单的select操做,属于快照读,不加锁。(固然,也有例外,下面会分析)
select * from table where ?;
当前读:特殊的读操做,插入/更新/删除操做,属于当前读,须要加锁。
select * from table where ? lock in share mode;select * from table where ? for update;insert into table values (…);update table set ? where ?;delete from table where ?;