MySQL事务隔离级别&锁

 
 
 

在MySQL中,默认的隔离级别是RR。

事务的隔离级别如下所示:

隔离级别 脏读 可重复读 幻象读
RU 存在 存在 存在
RC 不存在 存在 存在
RR 不存在 不存在 存在
Serializable 不存在 不存在 不存在

 

 

 

 

 

 

 

  • 脏读:当前事务可能读到其它事务未提交的数据。
  • 可重复读:当前事务内,多次读取同一份数据是一致的,不受其它事务的DML影响。
  • 幻象读:在RR隔离级别下,数据读出来没有,但是写不进去。

 

  • RC隔离级别下,支持读已提交事务的数据,其会在事务中每次查询时重新生成一致性视图,读取到其它事务提交后的数据,不支持可重复读。
  • RR隔离级别下,支持可重复读。会在第一个select开始时生成一个一致性视图,此后直至事务结束,这个过程中读不到其它事务在创建这个一致性视图之后提交的数据,这也是标准SQL在RR级别下为啥有幻象读的缘故。
  • Serializable隔离级别下,支持可重复读,无幻象读。因为读取时,会在表上加表锁读锁(S),编辑时,会在表上加表锁写锁(X),故支持可重复读,不会存在幻象读。

 

在MySQL的InnoDB实现下,RR级别,如果查询条件能使用上唯一索引,或者是一个唯一的查询条件,那么仅加行锁,如果是一个范围查询,那么就会给这个范围加上 gap 锁,解决了幻象读的问题。故MySQL上RR隔离级别不会有幻象读。标准SQL规范是RR下存在幻象读的,使用其它数据库引擎时需要关注其实现机制。

 

RC&RR加锁区别

  • RC隔离级别下,MySQL会在命中行上加行锁。但是如果无法命中索引时,存储引擎返回MySQL Server时,会在表的所有记录上加上行锁。在MySQL Server过滤数据时,不满足条件的记录,会释放掉行锁再返回。RC级别没有gap锁。
  • RR隔离级别下,MySQL会在命中行上加行锁,会在行间加gap锁。如果无法命中索引,会在表的所有记录上加行锁,且行之间加gap锁。在MySQL Server过滤数据时,不满足条件的记录不会释放行锁。

因为这些加锁上的区别,RC的性能要好于RR的性能。大部分互联网业务场景下,会选择使用RC作为业务使用的隔离级别。

 

MySQL的MVCC协议的镜像读&当前读

MVCC协议保证了并发下的查询性能&一致性。该协议会使用乐观锁机制,基于事务版本号,在每一个行记录上加上了2个隐藏列,分别记录创建时的事务版本号,删除时的事务版本号。

select时,会查询<=当前事务版本号的记录,过滤有删除版本号的记录。

insert时,会记录当前事务版本号。

delete时,会记录当前事务版本号为删除版本号。

update时,会先删除原始记录,在原始记录上记录删除版本号;然后新增新记录,在新纪录上记录新增版本号。

基于这个机制,那一般的select查询,都是镜像读,因为可能读取到历史版本,不一定是最新的版本。

特殊的select比如:

select * from xxx where ? lock in share mode(加S锁)

select * from xxx where ? for update(加X锁)

DML操作,都是当前读。当前读保证读取到记录的最新版本数据。

 

参考资料:

https://tech.meituan.com/2014/08/20/innodb-lock.html

http://www.cppcns.com/shujuku/mysql/251595.html

https://xie.infoq.cn/article/3b24c93c7e9280111780fac6c

https://my.oschina.net/alchemystar/blog/1927425

https://github.com/Yhzhtk/note/issues/42

http://www.itsoku.com/article/211

 

posted @ 2020-07-13 17:08  飞昂之雪  阅读(307)  评论(0编辑  收藏  举报