Mysql 隔离级别及实现
脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
未提交读:只有写的时候的行级锁。
读已提交:MVCC每次读都进行一次快照,行级锁。
可重复读:MVCC事务中的第一次读会对事务进行快照,行级锁间隙锁。
可串行化:表锁。
[锁机制](set tx_isolation=“REPEATABLE-READ”)
死锁
在两个事务中,第一个事务为某行上锁(update、delete等)之后,另外一个事务对另外一行数据也上锁后,当这两个事务分别要请求对方上锁的数据时会发生死锁。测试时发现,发生死锁时被请求的那个事务会抛出异常:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
通过 show engine innodb status;可以找到发生的死锁信息。
MVCC (Multi-Version Concurrency Control)
MVCC下读是不需要加锁的,也就是读写可以并发进行。
数据
MVCC会给表中每一行数据加两个字段:
创建该记录的事务编号:mysql对每一个执行的事务(begin后的第一个实际执行的命令),分配一个严格递增的事务Id。记为trx_id
回滚指针:将老的数据放到undo(回滚)日志,并在新纪录中的回滚指针中指向上一条数据的地址。记为roll_pointer
快照
MVCC对事务中的读操作会进行 整个库 的 快照,并且会生成一致性(同一个事务一致性,保证可重复读)的视图read-view。
所有未提交事务id的数组(包括最小的id即min_id)。
已创建的最大的事务id(max_id,可能是已提交事务也可能是未提交事务)。
快照比对规则
1如果undo日志中的数据的事务id(trx_id)< min_id,表示这个版本是已提交的事务生成的,数据可见。
2如果undo日志中的trx_id > max_id,表实这个版本是由将来启动的事务生成的是不可见的。也就是该事务生成快照之后创建的事务。
3如果事务id在两个之间:min_id <= trx_id <= max_id
1)如果trx_id在快照中的未提交事务的id数组中,表实还没有提交的事务生成的,只对查询事务也是该事务可见,否则不可见。
2)如果不在数组中,则是已提交的事务,可见。
在事务进行查询时会生成相应快照,根据事务当前的id和undo的存储规则(新的在前面),则可以优先找到最新的且不是之后的对于该事务的可见id。
过程
开始某个事物(事务号:10)插入一条数据。
事务11,更新该条数据(上行级锁,事务结束或回滚后取消),将name设置为了“Snack”
事务12(一定是事务)查询这条信息,则会生成整个库(当然也包含该条数据)的快照,此时read_view包含的数据为:
未提交事务的数组:[11]
max_id=11,min_id=11
按照比对规则我们可以知道结果是:
事务11,再次更新该条数据,将name设置为了“张伟”,并且进行回滚。
事务12再次查询这条信息,此时比对的仍然是开始时生成的read_view(在读已提交中每次读都会生成新的),也就是说此时事务12仍然认为事务11是未完成状态。读取的事务仍然是: