现在的服务都是分布式,MySQL的集群架构也是一样。那么MySQL的集群架构中有一个点是读写分离,而读写分离是基于binlog实现的。那么接下来就MySQL的读写分离和binlog为突破点进行分析为什么大厂中的默认隔离级别是RC。总体来说以时间线为基准进行讲解。
参考:互联网项目中mysql应该选什么事务隔离级别
在Oracle,SqlServer中都是选择读已提交(Read Commited)作为默认的隔离级别,为什么Mysql不选择读已提交(Read Commited)作为默认隔离级别,而选择可重复读(Repeatable Read)作为默认的隔离级别呢?
首先要明确一点:MySQL需要主从复制!。主从复制需要binlog和事务的隔离级别。
历史原因:5.0版本及之前binlog只支持STATEMENT这种格式。为了支持主从复制,那么事务的隔离级别只能是可重复读(Repeatable Read)。
因为当binlog为STATEMENT格式,且隔离级别为读已提交(Read Commited)时,会有bug导致主从库的数据不一致。
如下图所示,在主(master)上执行如下事务。
此时在主(master)上执行下列语句
select * from test;
输出如下
+---+
| b |
+---+
| 3 |
+---+
1 row in set
但是,你在此时在从(slave)上执行该语句,得出输出如下
Empty set
这样,你就出现了主从不一致性的问题!原因其实很简单,就是在master上执行的顺序为先删后插!而此时binlog为STATEMENT格式,它记录的顺序为先插后删!从(slave)同步的是binglog,因此从机执行的顺序和主机不一致!就会出现主从不一致!
如何解决? 解决方案有两种。
因此由于历史原因,mysql将默认的隔离级别设为可重复读(Repeatable Read),保证主从复制不出问题!
存在的问题:
主库的数据commit到binlog后则返回SUCCESS给客户端。因为binlog同步给slave是异步的,如果此时master提交而未同步给到slave,则数据发生丢失。
主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
首先明确一点:master上已提交,然后等待slave的ACK,最后返回给客户端结果。
存在的问题:
幻读:当用户发起一个事务,该事务已经写入redo日志和binlog日志,所以该数据为有效状态。在RC隔离级别下其他事务是可以读取到的。如果在等待slave的ack过程中binlog还没传输到slave上,则其他事务查询该数据为修改后的数据,此时master宕机。slave上升为master。此时在查询新master则查询不到数据,出现幻读。
数据丢失:提高数据的安全性,但不能完全避免数据丢失。
现在我们已经知道,在半同步环境下,主库是在事务提交之后等待Slave ACK,所以才会有数据不一致问题。所以这个Slave ACK在什么时间去等待,也是一个很关键的问题了。因此MySQL针对半同步复制的问题,在5.7.2引入了Loss-less Semi-Synchronous,在调用binlog sync之后,engine层commit之前等待Slave ACK。这样只有在确认Slave收到事务events后,事务才会提交。
首先明确一点:在等待ack的时候,master状态为未提交。
存在的问题:
在这种模式下解决了after_commit模式带来的幻读和数据丢失问题,因为主库没有提交事务。但也会有个问题,假设主库在存储引擎提交之前挂了,那么很明显这个事务是不成功的,但由于对应的Binlog已经做了Sync操作,从库已经收到了这些Binlog,并且执行成功,相当于在从库上多了数据,也算是有问题的,但多了数据,问题一般不算严重。