Apakah kebuntuan? Artikel berikut akan membawa anda memahami kebuntuan MySQL. Mari kita bincangkan tentang syarat yang diperlukan untuk kebuntuan dalam Mysql dan bagaimana untuk menyelesaikan masalah kebuntuan? Semoga ia membantu semua orang.
Kebuntuan merujuk kepada fakta bahawa dalam dua atau lebih proses atau utas yang berbeza, disebabkan persaingan untuk sumber yang sama atau komunikasi antara proses (atau utas), setiap utas Mereka tergantung dan menunggu setiap lain. Jika tiada daya luaran, ia akhirnya akan menyebabkan keseluruhan sistem runtuh.
Syarat eksklusif sumber
Merujuk kepada eksklusiviti bersama apabila berbilang transaksi bersaing untuk sumber yang sama, iaitu, sumber hanya diduduki oleh satu transaksi dalam tempoh masa, yang juga boleh dipanggil sumber eksklusif (seperti kunci baris).
Minta dan tahan syarat
bermakna kunci A telah diperolehi dalam transaksi a, tetapi kunci baharu adalah dicadangkan Kunci B diminta, dan kunci B sudah diduduki oleh transaksi lain b Pada masa ini, transaksi a akan menyekat, tetapi ia akan mengekalkan kunci A yang telah diperolehinya.
Tiada syarat pelucutan
bermakna kunci A telah diperolehi dalam transaksi a dan tidak boleh dikunci sebelum ianya diserahkan. Untuk melucutkannya, anda hanya boleh melakukan transaksi selepas digunakan dan kemudian melepaskannya sendiri.
Syarat perolehan kunci bersama
bermaksud apabila kebuntuan berlaku, mesti ada proses perolehan kunci bersama, iaitu Semasa urus niaga kunci pegangan A memperoleh kunci B, transaksi b memegang kunci B juga memperoleh kunci A, yang akhirnya membawa kepada pemerolehan bersama dan setiap transaksi disekat.
Andaikan terdapat senario pemindahan apabila akaun A memindahkan 50 yuan ke akaun B, juga pindahkan 30 yuan ke akaun A. Adakah terdapat kebuntuan dalam proses ini?
3.1 Penyataan penciptaan jadual
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 Memulakan data berkaitan
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 Proses pemindahan biasa
Sebelum bercakap tentang masalah kebuntuan, Mari kita lihat proses pemindahan biasa dahulu.
Dalam keadaan biasa, pengguna A memindahkan 50 yuan kepada pengguna B, yang boleh diselesaikan dalam satu transaksi kunci tulis diperlukan (untuk KEMASKINI) Kuncinya untuk mengelakkan perubahan transaksi lain daripada menyebabkan perubahan kami hilang dan menyebabkan data kotor.
Sql yang berkaitan adalah seperti berikut:
Anda perlu mematikan penyerahan automatik mysql sebelum membuka transaksi
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)
# 转账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;
Keputusan selepas pelaksanaan:
Anda dapat melihat bahawa kemas kini data adalah normal
3.4 Baki awal proses pemindahan kebuntuan
ialah:
Dengan mengandaikan bahawa senario ini wujud di bawah konkurensi tinggi, Apabila pengguna A memindahkan 50 yuan kepada pengguna B, pengguna B juga memindahkan 30 yuan kepada pengguna A.
Kemudian proses dan garis masa operasi program java kami adalah seperti berikut:
1 Pengguna A memindahkan 50 yuan kepada pengguna B, dan transaksi 1 perlu dibuka dalam program untuk melaksanakan sql Dan dapatkan baki A dan kunci data A.
# 事务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. Pengguna B memindahkan 30 yuan kepada pengguna A. Dia perlu membuka transaksi 2 dalam program untuk melaksanakan sql, mendapatkan baki B dan mengunci data B.
# 事务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. Laksanakan baki sql dalam transaksi 1
# 获取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;
Seperti yang anda lihat, data B diperoleh dalam transaksi 1 A tamat masa berlaku semasa menulis kunci. Mengapa ini berlaku? Terutamanya kerana kami telah memperoleh kunci tulis pada data B dalam transaksi 2 dalam langkah 2, jadi transaksi 1 tidak akan mendapat kunci tulis pada data B sebelum transaksi 2 dilakukan atau digulung semula.
4. Laksanakan baki sql dalam transaksi 2
# 获取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;
Begitu juga, dapatkan data A dalam transaksi 2 Tamat masa juga berlaku semasa menulis kunci itu. Oleh kerana kunci tulis pada data A telah diperoleh dalam transaksi 1 dalam langkah 1, transaksi 2 tidak akan mendapat kunci tulis pada data A sebelum transaksi 1 dilakukan atau digulung semula.
5、 为什么会出现这种情况呢?
主要是因为事务1和事务2存在相互等待获取锁的过程,导致两个事务都挂起阻塞,最终抛出获取锁超时的异常。
3.5 死锁导致的问题
众所周知,数据库的连接资源是很珍贵的,如果一个连接因为事务阻塞长时间不释放,那么后面新的请求要执行的sql也会排队等待,越积越多,最终会拖垮整个应用。一旦你的应用部署在微服务体系中而又没有做熔断处理,由于整个链路被阻断,那么就会引发雪崩效应,导致很严重的生产事故。
要想解决死锁问题,我们可以从死锁的四个必要条件入手。
由于资源独占条件和不剥夺条件是锁本质的功能体现,无法修改,所以咱们从另外两个条件尝试去解决。
4.1 打破请求和保持条件
根据上面定义可知,出现这个情况是因为事务1和事务2同时去竞争锁A和锁B,那么我们是否可以保证锁A和锁B一次只能被一个事务竞争和持有呢?
答案是肯定可以的。下面咱们通过伪代码来看看:
/** * 事务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变量:80\n" + "SELECT user_id,@A_balance:=balance from account where user_id = '" + userFrom + "' for UPDATE;\n" + "# 获取B 的余额并存入B_balance变量:60\n" + "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 打破相互获取锁条件(推荐)
要打破这个条件其实也很简单,那就是事务再获取锁的过程中保证顺序获取即可,也就是锁A始终在锁B之前获取。
我们来看看之前的伪代码怎么优化?
/** * 事务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变量:80\n" + "SELECT user_id,@A_balance:=balance from account where user_id = '" + userFrom + "' for UPDATE;\n" + "# 获取B 的余额并存入B_balance变量:60\n" + "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;"); }
假设事务1的入参为(A, B),事务2入参为(B, A),由于我们对两个用户参数进行了排序,所以在事务1中需要先获取锁A在获取锁B,事务2也是一样要先获取锁A在获取锁B,两个事务都是顺序获取锁,所以也就打破了相互获取锁的条件,最终完美解决死锁问题。
因为mysql在互联网中的大量使用,所以死锁问题还是经常会被问到,希望兄弟们能掌握这方面的知识,提高自己的竞争力。
【相关推荐:mysql视频教程】
Atas ialah kandungan terperinci Apakah kebuntuan? Mari kita bincangkan tentang pemahaman tentang kebuntuan MySQL. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!