vlambda博客
学习文章列表

互联网项目MySQL选择读已提交隔离级别


互联网项目MySQL选择读已提交隔离级别


MySQL的事务隔离级别:读未提交、读已提交、可重复读、串行化。

我们都知道MySQL的默认隔离级别是可重复读,避免了脏读、不可重复读的问题,那么为什么例如Oracl默认隔离级别是读已提交?

这里先说一个结论:MySQL在互联网项目中的隔离级别也是要设置成读已提交的。

MySQL的主从复制

MySQL的主从复制的binlog日志有三种格式:

  • statement:记录的是修改SQL语句
  • row:记录的是每行实际数据的变更
  • mixed:statement和row模式的混合

MySQL在5.0版本以前,binlog只支持STATEMENT这种格式!而这种格式在读已提交(Read Commited)这个隔离级别下主从复制是有bug的,因此Mysql将可重复读(Repeatable Read)作为默认的隔离级别!

有什么bug?

会出现了主从不一致性的问题!原因其实很简单,就是在master上执行的顺序为先删后插时!而此时binlog为STATEMENT格式,它记录的顺序为先插后删!从(slave)同步的是binglog,因此从机执行的顺序和主机不一致!就会出现主从不一致!

解决方案有两种:

(1)隔离级别设为可重复读(Repeatable Read),在该隔离级别下引入间隙锁。当Session1执行delete语句时,会锁住间隙。那么,Ssession2执行插入语句就会阻塞住!(2)将binglog的格式修改为row格式,此时是基于行的复制,自然就不会出现sql执行顺序不一样的问题!奈何这个格式在mysql5.1版本开始才引入。因此由于历史原因,mysql将默认的隔离级别设为可重复读(Repeatable Read),保证主从复制不出问题!

那么,当我们了解完mysql选可重复读(Repeatable Read)作为默认隔离级别的原因后,接下来我们将其和读已提交(Read Commited)进行对比,来说明为什么在互联网项目为什么将隔离级别设为读已提交(Read Commited)!

为什么互联网项目设置事务隔离级别为读已提交

  1. 缘由一:在RR隔离级别下,存在间隙锁,导致出现死锁的几率比RC大的多!
select * from test where id < 3 for update;

在RR隔离级别下,存在间隙锁,可以锁住(2,5)这个间隙,防止其他事务插入数据!而在RC隔离级别下,不存在间隙锁,其他事务是可以插入数据!ps:在RC隔离级别下并不是不会出现死锁,只是出现几率比RR低而已!

  1. 缘由二:在RR隔离级别下,条件列未命中索引会锁表!而在RC隔离级别下,只锁行.

  2. 缘由三:在RC隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性!在5.1.15的时候,innodb引入了一个概念叫做“semi-consistent”,减少了更新同一行记录时的冲突,减少锁等待。所谓半一致性读就是,一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层判断此版本是否满足update的where条件。若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)!具体表现如下: 此时有两个Session,Session1和Session2!

-- Session1执行

update test set color = 'blue' where color = 'red';
-- 先不Commit事务!
-- 与此同时Ssession2执行

update test set color = 'blue' where color = 'white';

session 2尝试加锁的时候,发现行上已经存在锁,InnoDB会开启semi-consistent read,返回最新的committed版本(1,red),(2,white),(5,red),(7,white)。MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)! 而在RR隔离级别下,Session2只能等待!

在RC级别下,不可重复读问题需要解决么?

不用解决,这个问题是可以接受的!毕竟你数据都已经提交了,读出来本身就没有太大问题!Oracle的默认隔离级别就是RC,你们改过Oracle的默认隔离级别么?

在RC级别下,主从复制用什么binlog格式?

OK,在该隔离级别下,用的binlog为row格式,是基于行的复制!Innodb的创始人也是建议binlog使用该格式!