Java基础、中级、高级、架构面试资料

MySQL MVCC 设计缺陷

SQL herman 2368浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog2,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领
腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云

熟悉 Oracle 的都知道 Oracle mvcc 里有一个概念:query restart,因此我 3 月份的时候发了一个微博!

MySQL query restart

我的回答:这是 MySQL MVCC 的一个设计缺陷:

虽然 SQL-92 规定了四种隔离级别,但是在引入 MVCC 后,RC/RR 都是 snapshot isolation 下的变体。至于对隔离性和一致性的吐槽,引用沈洵的话:“快照读以更低的代价实现了更高的并发度,却不得不委身在原有事务框架内。其实 ACID 也只是一个标准,并非真理。”

既然是 snapshot isolation,MySQL 有什么问题呢,直接上案例:

准备工作:

create table mvcc(x int auto_increment primary key,y int default 1);insert into mvcc(y)  select 1 from mvcc; -- many timesQuery OK, 2097152 rows affected (13.24 sec)Records: 2097152  Duplicates: 0  Warnings: 0

RC/RR && autocommit = 0

MySQL query restart

按照 snapshot isolation 来说 session2 的 trxid 大于 session1 的 trxid,从而 session2 的修改对 session1 应该是不可见的:即 session1 应该更新 0 行,但是 MySQL 在这里却对 y=2 进行了更新。

MySQL 官方在 5.5+ 的文档也针对该问题做出了 Note:

The snapshot of the database state applies to SELECT statements within a transaction, not necessarily to DML statements. If you insert or modify some rows and then commit that transaction, a DELETE or UPDATE statement issued from another concurrent REPEATABLE READ transaction could affect those just-committed rows, even though the session could not query them. If a transaction does update or delete rows committed by a different transaction, those changes do become visible to the current transaction.

根本原因在于 MySQL 在 update/delete/insert/select for update/select lock in share mode 时进行的是 current read(selectlocktype != LOCK_NONE) 而非 consistent read。而 Oracle 解决这个问题的方式是对比 current read 和 consistent read 来决定是否进行 query restart(细节参考 tom oracle 艺术);对于该案例,session1 在 T2 时更新到 x=3000000 时发现 y 发生了变化从而回滚了这个变更并进入 query restart,最终结果就是在 T3 时更新了 0 行。

因此,如果业务依赖事务但是又不清楚具体数据库的实现细节,很容易就掉到坑里了;比如这个案例,就产生了”错误的”更新。

下面针对 MySQL RR 给出一个非谓词更新的一个案例:如果不理解这块事务,在T3时想当然就会觉得结果应该是 6000,可能就犯错了。

RR && autocommit=0

MySQL query restart

总的来说事务的水还是比较深的,当面对分布式时,又是另外一番烧脑。

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » MySQL MVCC 设计缺陷