数据库隔离机制的实现
数据库隔离机制
- 串行化
- 可重复读
- 读已提交
- 读未提交
数据库事务可能导致的问题
- 脏读
- 不可重复读
- 幻读
脏读
事务A,先执行,处于未提交的状态:
insert into table values(1, xx);
事务B,后执行,也未提交:
select * from table ;
如果事务B能够读取到(1, xx)这条记录,事务A就对事务B产生了影响,这个影响叫做“脏读”,读到了未提交事务操作的记录。
不可重复读
事务A,先执行:
select * from table where id=1;
结果集为:
1, xx
事务B,后执行,并且提交:
update table set name=yy where id=1;
commit;
事务A,再次执行相同的查询:
select * from table where id=1;
结果集为:
1, yy
这次是已提交事务B对事务A产生的影响,这个影响叫做“不可重复读”,一个事务内相同的查询,得到了不同的结果。
和脏读区别在于,脏读在于读事务B还没有提交的数据,而不可重读是读取到事务B**已经提交**的数据
幻读
事务A,先执行:
update table set name=“hh” where id>3;
结果为:
OK row xx 表名成功影响多少行数据
事务B,后执行,并且提交:
insert into table values(11, uu);
commit;
事务A,然后再select一下:
select * from table where id>3
结果集为:
…
11,uu
…
事务A懵了,我特么不是id>3全部更新了吗
这次是已提交事务B对事务A产生的影响,这个影响叫做“幻读”。
数据库隔离机制的实现
一、读未提交
这个玩意应该没人使用,完全性的不加锁,select不加锁。最爽,最快,可惜数据脏读
二、串行化
这个最安全,select都是加锁,所有select语句都会被隐式的转化为in share mode. 这就意味着,如果前面有事务在做修改操作,那么就会阻塞。完全性的把读和写串行化了。但是这个并发性不高
三、读已提交
最使用最广的隔离机制
普通读是快照读
加锁的select, update, delete等语句,除了在外键约束检查以及重复键检查时会封锁区间,其他时刻都只使用记录锁;
此时,其他事务的插入依然可以执行,就可能导致,读取到幻影记录。也就是说一般都是记录锁,锁单记录,上面那个幻读例子中,id>3的有5条记录,只会单独锁5条,插入11的时候还是不会锁住。
四、可重复读
这是InnoDB默认的隔离级别
普通的select使用快照读
加锁的select(S或者X), update, delete等语句,它们的锁,依赖于它们是否在唯一索引上使用了唯一的查询条件,或者范围查询条件:
- 在唯一索引上使用唯一的查询条件,会使用记录锁
- 范围查询条件,会使用间隙锁与临键锁,锁住索引记录之间的范围,避免范围间插入记录,以避免产生幻影行记录,以及避免不可重复的读
总结
- 读未提交没有锁
- 读已提交,普通读都是快照读,加锁读和更新操作一般都是记录锁,除非外键和重复键检查 解决脏读问题
- 可重复读,普通读是快照,加锁读和更新操作如果是唯一索引上的唯一条件则记录,范围条件就是间隙锁和临键锁 解决幻读,不可重读的问题
- 串行化,读写串行化
纠正个问题
大多数资料上说幻读只能串行化解决,而可重复读只能解决不可重复读,解决不了幻读。
在《Mysql技术内幕,InnoDB存储引擎》 事务解释,innodb默认的可重复读的事务隔离机制,采用的是next-key lock(临键锁) 锁定范围,因此避免了幻读