
面试官:什么是脏读、幻读、不可重复读?说一说MySQL数据库中的事务隔离级别是怎样的?
在 Serializable 隔离级别下,解决了脏读、幻读、不可重复读全部问题。

前面我们提到过,Oracle只支持ANSI/ISO SQL定义的Serializable和Read Committed隔离级别。
但实际上,根据Oracle官方文档的介绍,Oracle支持三种隔离级别:Read Committed、Serializable 和 Read-Only。
官网链接:https://docs.oracle.com/cd/E11882_01/server.112/e40540/consist.htm#CNCPT621
Read-Only 隔离级别类似于序列化隔离级别,但只读事务甚至不允许在事务中进行数据修改。
因此,在这三种隔离级别中,Serializable 和 Read-Only 显然都不适合作为默认隔离级别,Oracle 只剩下 Read Committed 这个选择。
MySQL 默认使用RR(可重复读)隔离级别的原因是基于历史和技术考虑。
MySQL 主从复制是通过 binlog 日志进行数据同步的,而早期的版本中 binlog 记录的是SQL语句的原文。
而早期的版本的 statement 格式下,记录到 binlog 里的是SQL语句原文,因此可能会出现这样一种情况:
例如这样一条SQL语句:
mysql> delete from t /*comment*/ where a>=4 and b<=’2024-10-24′ limit 1;
在主库执行这条 SQL 语句的时候,用的是索引 a;而在备库执行这条 SQL 语句的时候,却使用了索引 b。
【MySQL 执行优化器会进行采样预估,在不同的MySQL库里,采样计算出来的预估结果不一样,会影响优化器的判断,由于优化器会进行成本分析,可能最终选择的索引不一样。】
而又因为这条 delete 语句带了 limit,很可能会出现主备数据不一致的情况。
因此,MySQL 认为这样写是有风险的。
另外,如果使用读已提交(Read Committed)或读未提交(Read Uncommitted)这两种隔离级别,是不会添加 Gap Lock 间隙锁的。
而主从复制过程中出现的事务乱序的问题,更加容易导致备库在SQL回滚之后与主库内容不一致。

(MySQL官网上提交的BUG截图)
为了解决这个问题,MySQL选择了可重复读(Repeatable Read)隔离级别作为默认选项。
可重复读隔离级别,在更新数据时会增加记录锁和间隙锁,可以避免事务乱序导致的数据不一致问题。
ERROR 1598 (HY000):
Binary logging not possible. Message:
Transaction level 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'
尽管 MySQL 默认使用 RR 隔离级别,是为了解决主从复制过程中的数据一致性问题,但在实际应用中,根据具体需求和性能考虑,可以选择适合的隔离级别。
一些大型互联网公司,如阿里等会根据自身需求,将数据库的隔离级别从默认的 RR 调整为 RC(读已提交)。
这是因为 RC 隔离级别可以提供更高的并发度和降低死锁发生的概率。
当然是,高并发!
然而,RC 隔离级别仍然存在幻读和不可重复读的问题。就需要自己解决了。
而且,很多时候,不可重复读问题其实是可以忽略的。
比如,读取到别的事务修改的值,其实问题不太大的,只要修改的时候的不基于错误数据就可以了。
所以,我们都会在核心表中增加乐观锁标记,更新的时候都要带上锁标记,进行乐观锁更新。
总结起来,MySQL 默认使用 RR 隔离级别是为了解决,主从复制过程中的数据一致性问题,但在实际应用中,需要根据具体需求和性能考虑,可以选择适合的隔离级别。
原文始发于微信公众号(程序员阿凯):阿里二面,为什么 MySQL 选择 Repeatable Read 作为默认隔离级别?
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/174270.html