:切!这谁没听过,不就是多版本并发控制么~
早在亘古时期,修真界就流传着一门mysql功法,将其修至小乘境界,足以纵横一方。。。不乏也有走火入魔者,为祸一方~
Serializable篇
强制事务排序,串行化执行事务。技能之间不存在冲突,就是滚键盘~能不能再快点呢,技能前摇那么长,一次性放俩多好^_^
Read Uncommitted篇
某个不知名大牛路过你这样搞一下试试:
事务1 | 事务2 |
select * from table where key = 1; | |
update table set name="da niu" where key = 1; | |
select * from table where key = 1; |
看到了没,事务1第一次查询的结果和第二次查询的结果还能一样吗,this is 脏读!
Read Committed篇
原来这就是脏读啊,那我等到事务2提交之后再读,不就ok了吗^_^
事务1 | 事务2 |
select * from table where key = 1; | |
update table set name="da niu" where key = 1; | |
commit | |
select * from table where key = 1; |
你能说事务1第二次读出来的数据是脏的吗?不能。但是事务一两次读取的数据结果仍然不同,这叫做不可重复读问题。
Repeatable Read篇
这还怎么搞。。。
据说是有个头秃哥,搞了叫Read View个东西出来。Read View可以认为是数据库的一个内部快照,根据事务的隔离级别,决定在事务开始时,该事务能看到什么信息。就是说通过Read View,事务可以知道此时此刻能看到哪个版本的数据记录(有可能不是最新版本的,也有可能是最新版本的)。可重复读、读已提交、读未提交,这几个隔离级别都会使用Read View。
ReadView中主要包含几个比较重要的字段:
creator_trx_id | 创建Read View事务的id |
trx_ids | 生成Read View时活跃的事务id集合 |
up_limit_id | trx_ids中最小的事务id |
low_limit_id | 生成Read View时下一个可分配的事务id |
有了这些id信息,即使事务2已提交的情况下,也不会出现可重复读的问题了:
事务1 | 事务2 |
select * from table where key = 1;(获取Read View) | |
update table set name="da niu" where key = 1; | |
commit | |
select * from table where key = 1;(获取Read View) |
第一次查找获取Read View,当前事务id为0,该行数据的trx_id小于事务1Read View中的up_limit_id,即该行数据为事务1创建之前产生的,故而对事务1可见;
第二次查找,该行数据的trx_id被事务2改为了自己的事务id,使得trx_id在事务1Read View的trx_ids中,说明改行数据由创建事务1时的其它活跃事务更新过,故而使其对事务1不可见;
:数据就没。。没了?
当然不会,还记得undo log吗,记录了数据库操作的执行逻辑。我们对这行数据再做一次相反的执行逻辑,就能拿到修改前的数据了^_^
类似的,如果在事务1创建之后,又有事务3被创建,并修改了key=1的数据,则该行记录的trx_id大于等于事务1Read View中的low_limit_id,也可以知晓当前数据被修改过来。继续根据undo log恢复以前版本的数据即可。
这就是多版本并发控制了。。。
幻读篇
那幻读又是啥呢??
前面说了不可重复读的问题,是说同一个事务中,多次查询同一条记录,查询结果不同的问题;
幻读其实也很简单,说的是同一个事务中,查询多次,查询结果记录条数不一致的问题;
明白了吧,一个是说记录本身的内容发生变化,另一个说的是记录的条数。
显然,通过MVCC,也能解决幻读的问题了