<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
死鎖指的是在兩個或兩個以上不同的程序或執行緒中,由於存在共同資源的競爭或程序(或執行緒)間的通訊而導致各個執行緒間相互掛起等待,如果沒有外力作用,最終會引發整個系統崩潰。
指多個事務在競爭同一個資源時存在互斥性,即在一段時間內某資源只由一個事務佔用,也可叫獨佔資源(如行鎖)。
指在一個事務a中已經獲得鎖A,但又提出了新的鎖B請求,而該鎖B已被其它事務b佔有,此時該事務a則會阻塞,但又對自己已獲得的鎖A保持不放。
指一個事務a中已經獲得鎖A,在未提交之前,不能被剝奪,只能在使用完後提交事務再自己釋放。
指在發生死鎖時,必然存在一個相互獲取鎖過程,即持有鎖A的事務a在獲取鎖B的同時,持有鎖B的事務b也在獲取鎖A,最終導致相互獲取而各個事務都阻塞。
假設存在一個轉賬情景,A賬戶給B賬戶轉賬50元的同時,B賬戶也給A賬戶轉賬30元,那麼在這過程中是否會存在死鎖情況呢?
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='賬戶餘額表';
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);
在說死鎖問題之前,咱們先來看看正常的轉賬過程。
正常情況下,A使用者給B使用者轉賬50元,可在一個事務內完成,需要先獲取A使用者的餘額和B使用者的餘額,因為之後需要修改這兩條資料,所以需要通過寫鎖(for UPDATE)鎖住他們,防止其他事務更改導致我們的更改丟失而引起髒資料。
相關sql如下:
開啟事務之前需要先把mysql的自動提交關閉
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;
執行後的結果:
可以看到資料更新都是正常的情況
初始化的餘額為:
假設在高並行情況下存在這種場景,A使用者給B使用者轉賬50元的同時,B使用者也給A使用者轉賬30元。
那麼我們的java程式操作的過程和時間線如下:
A使用者給B使用者轉賬50元,需在程式中開啟事務1來執行sql,並獲取A的餘額同時鎖住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;
B使用者給A使用者轉賬30元,需在程式中開啟事務2來執行sql,並獲取B的餘額同時鎖住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;
在事務1中執行剩下的sql
# 獲取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資料的寫鎖。
在事務2中執行剩下的sql
# 獲取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;
同理可得,在事務2中獲取A資料的寫鎖時也出現了超時情況。因為步驟1的時候已經在事務1中獲取到A資料的寫鎖了,那麼在事務1提交或回滾前事務2永遠都拿不到A資料的寫鎖。
為什麼會出現這種情況呢?
主要是因為事務1和事務2存在相互等待獲取鎖的過程,導致兩個事務都掛起阻塞,最終丟擲獲取鎖超時的異常。
眾所周知,資料庫的連線資源是很珍貴的,如果一個連線因為事務阻塞長時間不釋放,那麼後面新的請求要執行的sql也會排隊等待,越積越多,最終會拖垮整個應用。一旦你的應用部署在微服務體系中而又沒有做熔斷處理,由於整個鏈路被阻斷,那麼就會引發雪崩效應,導致很嚴重的生產事故。
要想解決死鎖問題,我們可以從死鎖的四個必要條件入手。由於資源獨佔條件和不剝奪條件是鎖本質的功能體現,無法修改,所以咱們從另外兩個條件嘗試去解決。
根據上面定義可知,出現這個情況是因為事務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變數: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(); }
上面的虛擬碼顯而易見可以解決死鎖問題,因為所有的事務都是通過分散式鎖來序列執行的。
那麼這樣就真的萬事大吉了嗎?
在小流量情況下看起來是沒問題的,但是在高並行場景下這裡將成為整個服務的效能瓶頸,因為即使你部署了再多的機器,但由於分散式鎖的原因,你的業務也只能序列進行,服務效能並不因為叢集部署而提高並行量,完全無法滿足分散式業務下快、準、穩的要求,所以咱們不妨換種方式來看看怎麼解決死鎖問題。
要打破這個條件其實也很簡單,那就是事務再獲取鎖的過程中保證順序獲取即可,也就是鎖A始終在鎖B之前獲取。我們來看看之前的虛擬碼怎麼優化?
/** * 事務1入參(A, B) * 事務2入參(B, A) **/ public void transferAccounts(String userFrom, String userTo) { // 對使用者A和B進行排序,讓userFrom始終為使用者A,userTo始終為使用者B int flag = 1; if (userFrom.hashCode() > userTo.hashCode()) { String tmp = userFrom; userFrom = userTo; userTo = tmp; flag = -1; } // 開啟事務 JDBC.excute("START TRANSACTION;"); // 執行轉賬sql JDBC.excute("# 獲取userFrom 的餘額並存入A_balance變數:80n" + "SELECT user_id,@A_balance:=balance from account where user_id = '" + userFrom + "' for UPDATE;n" + "# 獲取userTo 的餘額並存入B_balance變數:60n" + "SELECT user_id,@B_balance:=balance from account where user_id = '" + userTo + "' for UPDATE;n" + "n" + "# 修改userFrom 的餘額n" + "UPDATE account set balance = @A_balance - " + (flag * 50) + " where user_id = '" + userFrom + "';n" + "# 修改userTo 的餘額n" + "UPDATE account set balance = @B_balance + " + (flag * 50) + " where user_id = '" + userTo + "';n"); // 提交事務 JDBC.excute("COMMIT;"); }
假設事務1的入參為(A, B),事務2入參為(B, A),由於我們對兩個使用者引數進行了排序,所以在事務1中需要先獲取鎖A在獲取鎖B,事務2也是一樣要先獲取鎖A在獲取鎖B,兩個事務都是順序獲取鎖,所以也就打破了相互獲取鎖的條件,最終完美解決死鎖問題。
因為mysql在網際網路中的大量使用,所以死鎖問題還是經常會被問到,希望兄弟們能掌握這方面的知識,提高自己的競爭力。
最後,外出打工不易,希望各位兄弟找到自己心儀的工作,虎年發發發!
到此這篇關於Mysql超詳細講解死鎖問題的理解的文章就介紹到這了,更多相關Mysql 死鎖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45