昨晚在一场主题为 “MYSQL和PGSQL谁是世界第一” 的直播中,出现了这样一个桥段:
其中一位大佬提出MySQL一直存在一个问题或BUG,就是在 特定场景下事务无法保证原子性,并举出示例。
(如图)
2024-06-05T03:32:05.png
首先我的第一反应跟各位看官相同,大吃一惊!!!,怎么可能,起码这种场景大多数人应该没有遇到过,接着就是怀疑人生,毕竟这个是大佬在直播时提出来的,难道真有这个问题!!!
在大家百感交集的时候,薛老师也是直接晒文,之前就这个问题薛老师也是从吃惊到总结。数据库对比系列之三(PG事务与MySQL事务)原文连接。
阅读文章并同薛老师交流后有了结论。
但是本着学的越多会的越少的态度,咱们还是自己再做一遍实验,从而得出结论。

实验准备

一个干净的MySQL数据库就行,我这里用的是8.0.26

实验一:

首先就是DBA最常用的黑屏界面了。
操作步骤:

1:
CREATE TABLE ttt (
id int NOT NULL,
PRIMARY KEY (id)
);
2:
insert into ttt values(1);
3:
set autocommit=0;
begin;
insert into ttt values(1);
insert into ttt values(2);
commit;
4:
select * from ttt;

(结果如图)
2024-06-05T03:32:36.png
这。。。。。。好像真的不太对啊,由于事务中的第一个事件失败了,按理说整个事务应该失败回滚,为什么会1失败2成功呢,好像真的违背的原子性!
先不急着下结论,我们继续实验

实验二:

相同的数据相同的操作,这次在我们常用的 运维工具Navicat 中测试
步骤相同,直接看结果

(如图)
2024-06-05T03:32:52.png
Navicat看似好像合理,由于事件1报错,所以该事务中断了,并没有继续执行后面的内容。但是但是!千万小心,这里中断 并没有终止该事务,意思就是该事务还等着commit或者rollback
接着往下看。

实验三:

将两个事件的反过来

2024-06-05T03:33:11.png
可以看到事件1正常,事件2由于主键冲突,导致事务中断,还是一个道理中断非终止。可是如果现在该会话窗口再begin或任何触发了隐式提交,则事件1会插入成功,而不是因为事务中事件2失败导致整个事务自动回滚。
已经看蒙B的同学非分好事务和事件再跟着实验走一遍就明白了。

情况就是这么个情况了,那真的是MySQL的漏洞吗?

首先我们要确认几个问题:
1、谁能通过服务器直接访问我们线上数据库并操作事务,应该只有DBA才对
2、谁能通过Navicat直接操作我们线上数据库并执行事务,应该也只可能是DBA
既然是这样,那么MySQL则认为,在一个事务中有事件报错,我已经给你反馈了error(服务器端)或中止(Navicat端),那么剩下的选择权就交给你,如果你还是要commit,那么就commit执行成功的事件,如果rollback,那么就回滚整个事务

归根结底

其实就是理念问题,在上面薛老师的文章中也提到,这就是MySQL的一种设计理念,与之相对的PGSQL理念则是当一个事务中的任何事件只要出现异常则强制回滚整个事务。所以不能说这个MySQL的BUG,只能说其设计理念就是这样。
我们不妨再来个实验让大家更好理解

实验四:

1:
insert into ttt values(1);
2:
set autocommit=0;
begin;
insert into ttt values(1),(2);
commit;
3:
set autocommit=0;
begin;
insert into ttt values(2),(1);
commit;
4:
select * from ttt;

2024-06-05T03:33:32.png
这里的实验结果与实验一有区别,原因很简单,这里是一个事务中只有一个事件,执行异常你再commit也不会提交成功。

既然都说到这了,肯定有同学会想,DBA不骚整,开发骚整怎么办?
话不多说,直接上伪代码

实验五:

通过代码开启事务模拟如实验一的操作

(如图)
2024-06-05T03:33:51.png
可以看到,在框架中只要开启了事务都会做error处理,只要有失败则直接rollback(全部回滚),并不会出现事务中单个事件提交成功的情况

复盘结束

好了好了,到此结束,各位看官不必惊慌,这不能算MySQL的BUG或漏洞,也不必钻牛角尖说合不合理科不科学,只能说这种设计理念可能会让不少人产生误解。