MySQL 死锁后事务无法回滚是真的吗?

e297b8715a067444c90d131ba9546406.png

开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis ,Oracle ,Oceanbase 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请加微信号 liuaustin3 (共1200人左右 1 + 2 + 3)新人会进入3群

MySQL 作为目前互联网企业使用最多的,或者说在基于成本下,最流行的数据库之一,MySQL 在国内使用者众多,那么在MySQL偶然安装后,在使用中出现死锁后,死锁中的事务到底能不能回滚 ?我们来进行相关的实验

我们先验证一遍

1 我们打开一个MySQL 版本为 8.027 官方版本

823934a78ff042e28b5a951de61b8162.png

2 通过下面的操作我们可以确认两个分屏访问的是同一个MySQL的数据库

87187b6a38d43103275b56026501e285.png

3 我们在其中建立一张表,并且插入数据

714f816fd56905d2f6ec13b45ee3a39c.png

4  我们通过数据库的操作原理,产生了一个死锁条件,让下面的操作产生了死锁,并让数据通过自身的工作原理,解开了死锁。

37ef1dc7b48ebcfc09de8ab6dda4d4a3.png

5  然后我们对相关的表进行数据查询,看最终我们的数据表产生了什么最终的结果

bee86b856d4a223d110f757f66e5c227.png

6  我们再次验证,MySQL 数据库的隔离级别,我们并未选择MySQL的默认隔离级别 Repeatable Read ,而是我们大多数数据库包含Oracle 常用的 read committed

b7b41a72416806fbf1931d602299f0b3.png

好了现在我们来捋一捋结果,到底是不是如PostgreSQL 老师们Diss ,MySQL 存在死锁时,部分提交的问题,并且违反了事务的ACID的特性。

我们先把A 面的操作都用文本列出来

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27    |
+-----------+
1 row in set (0.00 sec)

mysql> use test;
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| orders         |
| payments       |
| test_deadlock  |
+----------------+
3 rows in set (0.00 sec)

mysql> drop table test_deadlock;
Query OK, 0 rows affected (0.04 sec)

mysql> create table test_deadlock (id int primary key,name varchar(255));
Query OK, 0 rows affected (0.02 sec)

mysql> insert into test_deadlock (id,name) values (1,'a');
Query OK, 1 row affected (0.01 sec)

mysql> insert into test_deadlock (id,name) values (2,'b');
Query OK, 1 row affected (0.00 sec)

mysql> select * from test_deadlock;
+----+------+
| id | name |
+----+------+
|  1 | a    |
|  2 | b    |
+----+------+
2 rows in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test_deadlock(id,name) values (3,'c');
Query OK, 1 row affected (0.01 sec)

mysql> update test_deadlock set name = 'd' where id = 1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update test_deadlock set name = 'd' where id = 1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1  Changed: 0  Warnings: 0

mysql> update test_deadlock set name = 'd' where id = 2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from test_deadlock;
+----+------+
| id | name |
+----+------+
|  1 | d    |
|  2 | e    |
|  3 | c    |
+----+------+
3 rows in set (0.00 sec)

mysql> show variables like '%isolation%';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+

我们在把B面的文字给大家展示

mysql> 
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27    |
+-----------+
1 row in set (0.00 sec)

mysql> use test;
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| orders         |
| payments       |
| test_deadlock  |
+----------------+
3 rows in set (0.00 sec)

mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| orders         |
| payments       |
+----------------+
2 rows in set (0.00 sec)

mysql> select * from test_deadlock;
+----+------+
| id | name |
+----+------+
|  1 | a    |
|  2 | b    |
+----+------+
2 rows in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> 
mysql> 
mysql> 
mysql> update test_deadlock set name = 'e' where id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update test_deadlock set name = 'd' where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test_deadlock;
+----+------+
| id | name |
+----+------+
|  1 | d    |
|  2 | e    |
|  3 | c    |
+----+------+
3 rows in set (0.00 sec)

mysql> show variables like '%isolation%';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+
1 row in set (0.00 sec)

结论,的确MySQL 在发生死锁的情况下,会有事务部分提交的问题,从上面的图和文本可以看出,A 事务中,插入数据和对数据第一行的修改,均生效了,而按照数据库的事务部分的既定原理,这是不可以的,事务要么回滚,要么全部执行。

实际上,这个问题和死锁本身关系不大,我们通过这样一个情景可以再次验证问题

d19b13ff7728f5d7470640b1e51a3f6a.png

我们在把文字进行展示

A 操作后的结果

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from test_deadlock where id = 1;
Query OK, 1 row affected (0.00 sec)

mysql> insert into test_deadlock (id,name) values (3,'d');
ERROR 1062 (23000): Duplicate entry '3' for key 'test_deadlock.PRIMARY'
mysql> 
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test_deadlock;
+----+------+
| id | name |
+----+------+
|  1 | d    |
|  2 | e    |
|  3 | c    |
+----+------+
3 rows in set (0.00 sec)

B 操作后的结果

1 row in set (0.00 sec)

mysql> 
mysql> 
mysql> 
mysql> 
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from test_deadlock where id = 1;
Query OK, 1 row affected (0.01 sec)

mysql> insert into test_deadlock (id,name) values (3,'d');
ERROR 1062 (23000): Duplicate entry '3' for key 'test_deadlock.PRIMARY'
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test_deadlock;
+----+------+
| id | name |
+----+------+
|  2 | e    |
|  3 | c    |
+----+------+
2 rows in set (0.00 sec)

从上的结果看,实际上在事务出现操作错误后,按照数据库原理是应该全部回滚的,而B 操作中,我们commit 是会部分进行提交的,也就是把错误的操作不提交,而正确的操作提交。

结论最终的结果是------是的MySQL 在事务操作中,并不是按照我们认为的数据库原理进行事务的操作的,或者更准确的说,一部分不是我们认为的那样。

这里我们也有其他类似的设计,这个数据库就是 SQL SERVER ,使用过SQL SERVER 的小伙伴,不知道注意到没有SQL SERVER 本身默认安装后,也是和MySQL 一样的,事务错误后会部分提交事务中错误发生前正确的部分。

这样的设计有一种说法,MySQL本身就不是给金融类产品使用的,而是定位于WEB 类型的数据库产品,而web 类的数据库产品本身就更具有灵活性,同时需要更快的速度,将很多问题都简化和改变的情况,这里我们可以从MySQL 在处理事务的和存储过程的建议,或者说民间使用MySQL的建议,不建议大事务,不建议存储过程大量的在业务中使用的这个‘口耳相传’的论调,找寻到一些最初MySQL 被设计出来的最初的初衷。

所以数据库本身是有定位的,不是一个数据库走天下,不同的数据库定位的业务不同,定位的使用者和业务的逻辑不同。当然事务不完全回滚对于使用者本身是不是一个问题,具体我们需要看业务的设定是不是允许或接受,如同MySQL 本身也在表设计时也不希望遵循 三范式一样。MySQL 是一个反传统的数据库产品。

但是 但是 但是  MySQL 可以解决这个问题,我们有参数可以解决。这点和SQL SERVER 是一样的。

5810426a13a33c5ea630e669885c3d9b.png

经过我们的调整MySQL的参数后,MySQL 满足了我们传统的数据库对于事务中的要求,要么全回滚,要么全不回滚。

21e1373b95b4674d0631ea124e965c2e.png

A 和 B 事务代码,事务A 中的插入是没有生效的,从而证明MySQL 完全可以实现在死锁后死锁事务的全部回滚。顺便打一个广告 2023-08-22日晚8点,大家可以了解PolarDB 了解一下,我们请来PolarDB的基础架构研发负责人 来回答大家对于PolarDB数据库的问题,吃不了亏,上不了当 !(具体参见地址在文章最下方)

备注:关于SQL SERVER 部分提交的问题,感兴趣加群,告诉你怎么解决

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test_deadlock(id,name) values (4,'g');
Query OK, 1 row affected (0.00 sec)

mysql> update test_deadlock set name = 'd' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update test_deadlock set name = 'e' where id = 2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test_deadlock;
+----+------+
| id | name |
+----+------+
|  1 | d    |
|  2 | e    |
|  3 | c    |
+----+------+
3 rows in set (0.00 sec)


mysql> 
mysql> select * from test_deadlock;
+----+------+
| id | name |
+----+------+
|  1 | a    |
|  2 | e    |
|  3 | c    |
+----+------+
3 rows in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update test_deadlock set name = 'e' where id = 2;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1  Changed: 0  Warnings: 0

mysql> update test_deadlock set name = 'd' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值