对这个问题的思考起源于一篇文字,文字中针对 MYSQL的隔离级别中的实现的问题进行了说明 MySQL Repeatable-Read 隔离级别一些误解 - 知乎 (zhihu.com) ,里面写的很详细,这里就不在详述了,感兴趣的同学可以去看,很涨知识,例如我,因为读了这篇文字,对于 PG MYSQL 在MVCC 的实现的问题,有了更深的认知。顺便说一句,写这篇文字的同学是阿里云POLARDB 的数据库开发者。https://zhuanlan.zhihu.com/p/402008512
在读完这篇文字后,PG 和 MYSQL 的在RR 方式的优缺点(姑且叫优缺点),被搬上了桌面。这里文字中提出了一个问题,就是MYSQL 对比PG 虽然在高并发的事务工作中占有优势,随之产生了一个 lost update 的问题,那么什么是lost update ,这点在数据库中实现中,是否可以被接受(实际上不接受也的接受),那么数据库的使用者,是否了解这一点,并且因为这点来选择你使用的数据库类型,或者在应用程序中进行弥补,是这篇文字需要进行思考的。
那么我们需要第一个要了解的就是什么是 lost update,lost update 会产生什么问题。
先从名词上来解释一下什么是LOST UPDATE
1 LOST UPDATE 发生在至少两个SESSION,发起了对同样行的操作事务,并且在重叠的时间内。
2 一个SESSION 对这一行进行了修改,而另一个事务在第一个事务并未commit 前,也更改了这一行的值,然后进行了commit 。
3 此时我们产生了一个问题,我们丢失了第一个事务对这个行的值的UPDATE的过程。
我们对上面的解释进行一个具体的操作,操作的MYSQL版本为 MYSQL 8.027 社区版
设置初始化环节
具体的操作过程,我们可以发现整体的操作过程中,MYSQL 虽然通过锁超时的方式,在 SESSION 1 并未commit 前阻止了 SESSION 2 的commit ,但是SESSION 1 COMMIT 后,SESSION 2 就可以进行COMMIT ,
这里强调的是如何进行数据的更新,如果是 POSTGRESQL 这样的操作,直接会反馈事务无法完成,并且SESSION 2 会直接 ROLLBACK , 而MYSQL 则可以进行COMMIT (这里不考虑由于lock wait 的原因导致错误信息)
这里牵扯两个问题,也是基于题目当中的问题,两种处理RR 在事务重叠处理的方式,那个更好。
PG 方: 这样的情况,事务2 直接失败
MYSQL 方: 这样的方式,可以有通过的可能
从上面的理解来看,MYSQL 占优势,因为事务是可以通过这样的方式降低成本,尤其是高并发使用RR 时的事务操作成本。
那么下面我们在做一个实验,我们看看,这样的情况MYSQL 会发生什么。
1 推测 按照逻辑推断, TX2 中必然发生 无法更新的错误,因为我们数据表中已经有了主键为2 的记录,直接进行数据的更新,会报错。(同样适用于唯一索引的类型)
2 实际操作,实际上不是,我们可以看到直接UPDATE 是成功的,显示0行操作。
那么问题来了,如果是银行操作呢,如果是现金操作呢,出事了,数据丢失了,MYSQL 此时作何答复????
如果是PG ORACLE ,则这样的更新会被屏蔽掉,虽然导致COST 很大,但是至少不会出现漏洞,而MYSQL 的为了降低COST 而产生了数据风险。
另外可以通过观察,MYSQL 在判断的时候,一定不是基于SNAPSHOT 数据更新的方式,这点在上面那个老师的文字中证明了,那么他在上面的情况中,基于数据的问题 first-committer-wins 这样的理论在这个问题中出现了严重的问题,因为第二个事务应该报错,报无法更新的问题,这样应用程序就可以获知,进行处理,而不幸的是第二个事务,竟然update 成功了(那么唯一索引,主键我可以认为是失效了吗),这样应用程序不会报错,但实际的业务操作了,你觉得这样的处理问题的方式能被接受吗?(因为应用程序会在他自己的操作日志里面展现,这个操作成功),你能忍吗?
反正我是不能,所以如果我使用MYSQL 还是老老实实的使用 RC ,或者使用PG 来处理RR 的需求,当然 PG 也不是没有问题,如果你敢使用RR 那么COST 在高并发的情况下,会给你展现 Serialzation的问题,应用也应该是慌得一匹。