我是靠谱客的博主 鲤鱼指甲油,这篇文章主要介绍什么是死锁?聊聊对MySQL死锁的理解,现在分享给大家,希望可以做个参考。

什么是死锁?下面本篇文章带大家了解一下MySQL死锁。聊聊Mysql出现死锁的必要条件,如何解决死锁问题?希望对大家有所帮助。

1、什么是死锁?

2、Mysql出现死锁的必要条件

  • 资源独占条件

  • 请求和保持条件

  • 不剥夺条件

  1. 相互获取锁条件

3、 Mysql经典死锁案例

3.1 建表语句

复制代码
1
2
3
4
5
6
7
CREATE TABLE `account` ( `id` int(11) NOT NULL COMMENT '主键', `user_id` varchar(56) NOT NULL COMMENT '用户id', `balance` float(10,2) DEFAULT NULL COMMENT '余额', PRIMARY KEY (`id`), UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='账户余额表';
登录后复制

3.2 初始化相关数据

复制代码
1
2
INSERT INTO `test`.`account` (`id`, `user_id`, `balance`) VALUES (1, 'A', 80.00); INSERT INTO `test`.`account` (`id`, `user_id`, `balance`) VALUES (2, 'B', 60.00);
登录后复制

3.3 正常转账过程

开启事务之前需要先把mysql的自动提交关闭

复制代码
1
2
set autocommit=0; # 查看事务自动提交状态状态
登录后复制

show VARIABLES like 'autocommit';![在这里插入图片描述](https://img-blog.csdnimg.cn/a486a4ed5c9d4240bd115ac7b3ce5a39.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_

Q1NETiBA6ZqQIOmjjg==,size_20,color_FFFFFF,t_70,g_se,x_16)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
# 转账sql START TRANSACTION; # 获取A 的余额并存入A_balance变量:80 SELECT user_id,@A_balance:=balance from account where user_id = 'A' for UPDATE; # 获取B 的余额并存入B_balance变量:60 SELECT user_id,@B_balance:=balance from account where user_id = 'B' for UPDATE; # 修改A 的余额 UPDATE account set balance = @A_balance - 50 where user_id = 'A'; # 修改B 的余额 UPDATE account set balance = @B_balance + 50 where user_id = 'B'; COMMIT;
登录后复制

执行后的结果:

1.jpg

可以看到数据更新都是正常的情况

3.4 死锁转账过程

初始化的余额为:

2.jpg

那么我们的java程序操作的过程和时间线如下:

1、A用户给B用户转账50元,需在程序中开启事务1来执行sql,并获取A的余额同时锁住A这条数据。

复制代码
1
2
3
4
5
# 事务1 set autocommit=0; START TRANSACTION; # 获取A 的余额并存入A_balance变量:80 SELECT user_id,@A_balance:=balance from account where user_id = 'A' for UPDATE;
登录后复制

2、B用户给A用户转账30元,需在程序中开启事务2来执行sql,并获取B的余额同时锁住B这条数据。

复制代码
1
2
3
4
5
# 事务2 set autocommit=0; START TRANSACTION; # 获取A 的余额并存入A_balance变量:60 SELECT user_id,@A_balance:=balance from account where user_id = 'B' for UPDATE;
登录后复制

3、在事务1中执行剩下的sql

复制代码
1
2
3
4
5
6
7
8
# 获取B 的余额并存入B_balance变量:60 SELECT user_id,@B_balance:=balance from account where user_id = 'B' for UPDATE; # 修改A 的余额 UPDATE account set balance = @A_balance - 50 where user_id = 'A'; # 修改B 的余额 UPDATE account set balance = @B_balance + 50 where user_id = 'B'; COMMIT;
登录后复制

可以看到,在事务1中获取B数据的写锁时出现了超时情况。为什么会这样呢?主要是因为我们在步骤2的时候已经在事务2中获取到B数据的写锁了,那么在事务2提交或回滚前事务1永远都拿不到B数据的写锁。

4、在事务2中执行剩下的sql

复制代码
1
2
3
4
5
6
7
8
# 获取A 的余额并存入B_balance变量:60 SELECT user_id,@B_balance:=balance from account where user_id = 'A' for UPDATE; # 修改B 的余额 UPDATE account set balance = @A_balance - 30 where user_id = 'B'; # 修改A 的余额 UPDATE account set balance = @B_balance + 30 where user_id = 'A'; COMMIT;
登录后复制

5、 为什么会出现这种情况呢?

3.5 死锁导致的问题

4、如何解决死锁问题?

4.1 打破请求和保持条件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** * 事务1入参(A, B) * 事务2入参(B, A) **/ public void transferAccounts(String userFrom, String userTo) { // 获取分布式锁 Lock lock = Redisson.getLock(); // 开启事务 JDBC.excute("START TRANSACTION;"); // 执行转账sql JDBC.excute("# 获取A 的余额并存入A_balance变量:80n" + "SELECT user_id,@A_balance:=balance from account where user_id = '" + userFrom + "' for UPDATE;n" + "# 获取B 的余额并存入B_balance变量:60n" + "SELECT user_id,@B_balance:=balance from account where user_id = '" + userTo + "' for UPDATE;n" + "n" + "# 修改A 的余额n" + "UPDATE account set balance = @A_balance - 50 where user_id = '" + userFrom + "';n" + "# 修改B 的余额n" + "UPDATE account set balance = @B_balance + 50 where user_id = '" + userTo + "';n"); // 提交事务 JDBC.excute("COMMIT;"); // 释放锁 lock.unLock(); }
登录后复制

那么这样就真的万事大吉了吗?

4.2 打破相互获取锁条件(推荐)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/** * 事务1入参(A, B) * 事务2入参(B, A) **/ public void transferAccounts(String userFrom, String userTo) { // 对用户A和B进行排序,让userFrom始终为用户A,userTo始终为用户B if (userFrom.hashCode() > userTo.hashCode()) { String tmp = userFrom; userFrom = userTo; userTo = tmp; } // 开启事务 JDBC.excute("START TRANSACTION;"); // 执行转账sql JDBC.excute("# 获取A 的余额并存入A_balance变量:80n" + "SELECT user_id,@A_balance:=balance from account where user_id = '" + userFrom + "' for UPDATE;n" + "# 获取B 的余额并存入B_balance变量:60n" + "SELECT user_id,@B_balance:=balance from account where user_id = '" + userTo + "' for UPDATE;n" + "n" + "# 修改A 的余额n" + "UPDATE account set balance = @A_balance - 50 where user_id = '" + userFrom + "';n" + "# 修改B 的余额n" + "UPDATE account set balance = @B_balance + 50 where user_id = '" + userTo + "';n"); // 提交事务 JDBC.excute("COMMIT;"); }
登录后复制

5、总结

因为mysql在互联网中的大量使用,所以死锁问题还是经常会被问到,希望兄弟们能掌握这方面的知识,提高自己的竞争力。

【相关推荐:mysql视频教程】

以上就是什么是死锁?聊聊对MySQL死锁的理解的详细内容,更多请关注靠谱客其它相关文章!

最后

以上就是鲤鱼指甲油最近收集整理的关于什么是死锁?聊聊对MySQL死锁的理解的全部内容,更多相关什么是死锁?聊聊对MySQL死锁内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(90)

评论列表共有 0 条评论

立即
投稿
返回
顶部