欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mysql超詳細(xì)講解死鎖問題的理解

 更新時間:2022年03月31日 18:17:46   作者:Mysql死鎖  
又到了金三銀四的時候,大家都按耐不住內(nèi)心的躁動,我在這里給大家分享下之前面試中遇到的一個知識點(diǎn)(死鎖問題),如有不足,歡迎大佬們指點(diǎn)指點(diǎn)

1、什么是死鎖?

死鎖指的是在兩個或兩個以上不同的進(jìn)程或線程中,由于存在共同資源的競爭或進(jìn)程(或線程)間的通訊而導(dǎo)致各個線程間相互掛起等待,如果沒有外力作用,最終會引發(fā)整個系統(tǒng)崩潰。

2、Mysql出現(xiàn)死鎖的必要條件

資源獨(dú)占條件

指多個事務(wù)在競爭同一個資源時存在互斥性,即在一段時間內(nèi)某資源只由一個事務(wù)占用,也可叫獨(dú)占資源(如行鎖)。

請求和保持條件

指在一個事務(wù)a中已經(jīng)獲得鎖A,但又提出了新的鎖B請求,而該鎖B已被其它事務(wù)b占有,此時該事務(wù)a則會阻塞,但又對自己已獲得的鎖A保持不放。

不剝奪條件

指一個事務(wù)a中已經(jīng)獲得鎖A,在未提交之前,不能被剝奪,只能在使用完后提交事務(wù)再自己釋放。

相互獲取鎖條件

指在發(fā)生死鎖時,必然存在一個相互獲取鎖過程,即持有鎖A的事務(wù)a在獲取鎖B的同時,持有鎖B的事務(wù)b也在獲取鎖A,最終導(dǎo)致相互獲取而各個事務(wù)都阻塞。

3、 Mysql經(jīng)典死鎖案例

假設(shè)存在一個轉(zhuǎn)賬情景,A賬戶給B賬戶轉(zhuǎn)賬50元的同時,B賬戶也給A賬戶轉(zhuǎn)賬30元,那么在這過程中是否會存在死鎖情況呢?

3.1 建表語句

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 初始化相關(guān)數(shù)據(jù)

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 正常轉(zhuǎn)賬過程

在說死鎖問題之前,咱們先來看看正常的轉(zhuǎn)賬過程。

正常情況下,A用戶給B用戶轉(zhuǎn)賬50元,可在一個事務(wù)內(nèi)完成,需要先獲取A用戶的余額和B用戶的余額,因為之后需要修改這兩條數(shù)據(jù),所以需要通過寫鎖(for UPDATE)鎖住他們,防止其他事務(wù)更改導(dǎo)致我們的更改丟失而引起臟數(shù)據(jù)。

相關(guān)sql如下:

開啟事務(wù)之前需要先把mysql的自動提交關(guān)閉

set autocommit=0;
# 查看事務(wù)自動提交狀態(tài)狀態(tài)
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)

# 轉(zhuǎn)賬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;

執(zhí)行后的結(jié)果:

在這里插入圖片描述

可以看到數(shù)據(jù)更新都是正常的情況

3.4 死鎖轉(zhuǎn)賬過程

初始化的余額為:

在這里插入圖片描述

假設(shè)在高并發(fā)情況下存在這種場景,A用戶給B用戶轉(zhuǎn)賬50元的同時,B用戶也給A用戶轉(zhuǎn)賬30元。

那么我們的java程序操作的過程和時間線如下:

A用戶給B用戶轉(zhuǎn)賬50元,需在程序中開啟事務(wù)1來執(zhí)行sql,并獲取A的余額同時鎖住A這條數(shù)據(jù)。

# 事務(wù)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用戶轉(zhuǎn)賬30元,需在程序中開啟事務(wù)2來執(zhí)行sql,并獲取B的余額同時鎖住B這條數(shù)據(jù)。

# 事務(wù)2
set autocommit=0;
START TRANSACTION;
# 獲取A 的余額并存入A_balance變量:60
SELECT user_id,@A_balance:=balance from account where user_id = 'B' for UPDATE;

在事務(wù)1中執(zhí)行剩下的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;

在這里插入圖片描述

可以看到,在事務(wù)1中獲取B數(shù)據(jù)的寫鎖時出現(xiàn)了超時情況。為什么會這樣呢?主要是因為我們在步驟2的時候已經(jīng)在事務(wù)2中獲取到B數(shù)據(jù)的寫鎖了,那么在事務(wù)2提交或回滾前事務(wù)1永遠(yuǎn)都拿不到B數(shù)據(jù)的寫鎖。

在事務(wù)2中執(zhí)行剩下的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;

在這里插入圖片描述

同理可得,在事務(wù)2中獲取A數(shù)據(jù)的寫鎖時也出現(xiàn)了超時情況。因為步驟1的時候已經(jīng)在事務(wù)1中獲取到A數(shù)據(jù)的寫鎖了,那么在事務(wù)1提交或回滾前事務(wù)2永遠(yuǎn)都拿不到A數(shù)據(jù)的寫鎖。

為什么會出現(xiàn)這種情況呢?

主要是因為事務(wù)1和事務(wù)2存在相互等待獲取鎖的過程,導(dǎo)致兩個事務(wù)都掛起阻塞,最終拋出獲取鎖超時的異常。

在這里插入圖片描述

3.5 死鎖導(dǎo)致的問題

眾所周知,數(shù)據(jù)庫的連接資源是很珍貴的,如果一個連接因為事務(wù)阻塞長時間不釋放,那么后面新的請求要執(zhí)行的sql也會排隊等待,越積越多,最終會拖垮整個應(yīng)用。一旦你的應(yīng)用部署在微服務(wù)體系中而又沒有做熔斷處理,由于整個鏈路被阻斷,那么就會引發(fā)雪崩效應(yīng),導(dǎo)致很嚴(yán)重的生產(chǎn)事故。

4、如何解決死鎖問題?

要想解決死鎖問題,我們可以從死鎖的四個必要條件入手。由于資源獨(dú)占條件和不剝奪條件是鎖本質(zhì)的功能體現(xiàn),無法修改,所以咱們從另外兩個條件嘗試去解決。

4.1 打破請求和保持條件

根據(jù)上面定義可知,出現(xiàn)這個情況是因為事務(wù)1和事務(wù)2同時去競爭鎖A和鎖B,那么我們是否可以保證鎖A和鎖B一次只能被一個事務(wù)競爭和持有呢?答案是肯定可以的。下面咱們通過偽代碼來看看:

/**
* 事務(wù)1入?yún)?A, B)
* 事務(wù)2入?yún)?B, A)
**/
public void transferAccounts(String userFrom, String userTo) {
     // 獲取分布式鎖
     Lock lock = Redisson.getLock();
     // 開啟事務(wù)
     JDBC.excute("START TRANSACTION;");
     // 執(zhí)行轉(zhuǎn)賬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");
     // 提交事務(wù)
     JDBC.excute("COMMIT;");
     // 釋放鎖
     lock.unLock();
}

上面的偽代碼顯而易見可以解決死鎖問題,因為所有的事務(wù)都是通過分布式鎖來串行執(zhí)行的。

那么這樣就真的萬事大吉了嗎?

在小流量情況下看起來是沒問題的,但是在高并發(fā)場景下這里將成為整個服務(wù)的性能瓶頸,因為即使你部署了再多的機(jī)器,但由于分布式鎖的原因,你的業(yè)務(wù)也只能串行進(jìn)行,服務(wù)性能并不因為集群部署而提高并發(fā)量,完全無法滿足分布式業(yè)務(wù)下快、準(zhǔn)、穩(wěn)的要求,所以咱們不妨換種方式來看看怎么解決死鎖問題。

4.2 打破相互獲取鎖條件(推薦)

要打破這個條件其實(shí)也很簡單,那就是事務(wù)再獲取鎖的過程中保證順序獲取即可,也就是鎖A始終在鎖B之前獲取。我們來看看之前的偽代碼怎么優(yōu)化?

/**
* 事務(wù)1入?yún)?A, B)
* 事務(wù)2入?yún)?B, A)
**/
public void transferAccounts(String userFrom, String userTo) {
     // 對用戶A和B進(jìn)行排序,讓userFrom始終為用戶A,userTo始終為用戶B
     int flag = 1;
     if (userFrom.hashCode() > userTo.hashCode()) {
         String tmp = userFrom;
         userFrom = userTo;
         userTo = tmp;
         flag = -1;
     }
     // 開啟事務(wù)
     JDBC.excute("START TRANSACTION;");
     // 執(zhí)行轉(zhuǎn)賬sql
     JDBC.excute("# 獲取userFrom  的余額并存入A_balance變量:80\n" +
             "SELECT user_id,@A_balance:=balance from account where user_id = '" + userFrom + "' for UPDATE;\n" +
             "# 獲取userTo  的余額并存入B_balance變量:60\n" +
             "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");
     // 提交事務(wù)
     JDBC.excute("COMMIT;");
 }

假設(shè)事務(wù)1的入?yún)?A, B),事務(wù)2入?yún)?B, A),由于我們對兩個用戶參數(shù)進(jìn)行了排序,所以在事務(wù)1中需要先獲取鎖A在獲取鎖B,事務(wù)2也是一樣要先獲取鎖A在獲取鎖B,兩個事務(wù)都是順序獲取鎖,所以也就打破了相互獲取鎖的條件,最終完美解決死鎖問題。

5、總結(jié)

因為mysql在互聯(lián)網(wǎng)中的大量使用,所以死鎖問題還是經(jīng)常會被問到,希望兄弟們能掌握這方面的知識,提高自己的競爭力。

最后,外出打工不易,希望各位兄弟找到自己心儀的工作,虎年發(fā)發(fā)發(fā)!

到此這篇關(guān)于Mysql超詳細(xì)講解死鎖問題的理解的文章就介紹到這了,更多相關(guān)Mysql 死鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mysql數(shù)據(jù)庫mysql: [ERROR] unknown option ''--skip-grant-tables''

    mysql數(shù)據(jù)庫mysql: [ERROR] unknown option ''--skip-grant-tables'

    這篇文章主要介紹了mysql數(shù)據(jù)庫mysql: [ERROR] unknown option '--skip-grant-tables',需要的朋友可以參考下
    2020-03-03
  • MySQL中從庫延遲狀況排查的一則案例

    MySQL中從庫延遲狀況排查的一則案例

    這篇文章主要介紹了MySQL中從庫延遲狀況排查的一則案例,針對其從庫無業(yè)務(wù)狀態(tài)下的CPU大量占用情況,需要的朋友可以參考下
    2015-05-05
  • MySQL 5.6 中的 TIMESTAMP 和 explicit_defaults_for_timestamp 參數(shù)

    MySQL 5.6 中的 TIMESTAMP 和 explicit_defaults_for_timestamp 參數(shù)

    這篇文章主要介紹了MySQL 5.6 中的 TIMESTAMP 和 explicit_defaults_for_timestamp 參數(shù),需要的朋友可以參考下
    2015-08-08
  • MySQL空間函數(shù)ST_Distance_Sphere()的使用方式

    MySQL空間函數(shù)ST_Distance_Sphere()的使用方式

    這篇文章主要介紹了MySQL空間函數(shù)ST_Distance_Sphere()的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • MySQL全局鎖和表鎖的深入理解

    MySQL全局鎖和表鎖的深入理解

    這篇文章主要給大家介紹了關(guān)于MySQL全局鎖和表鎖的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用MySQL具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • MySql日期查詢語句詳解

    MySql日期查詢語句詳解

    在mysql中對時間日期操作的函數(shù)有很多,有時我們就希望直接通過sql查詢出指定日期的數(shù)據(jù)
    2013-11-11
  • MySQL5.7 集群配置的步驟

    MySQL5.7 集群配置的步驟

    這篇文章主要介紹了MySQL5.7 集群配置的步驟,幫助大家更好的理解和學(xué)習(xí)使用MySQL,感興趣的朋友可以了解下
    2021-03-03
  • MySQL中的行級鎖定示例詳解

    MySQL中的行級鎖定示例詳解

    這篇文章主要給大家介紹了關(guān)于MySQL中行級鎖定的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用MySQL具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • Linux環(huán)境下安裝MySQL8.0的完整步驟

    Linux環(huán)境下安裝MySQL8.0的完整步驟

    數(shù)據(jù)庫想必大家都很熟悉,但是要在服務(wù)器上自己來安裝數(shù)據(jù)庫,還是會出現(xiàn)不少的問題,下面這篇文章主要給大家介紹了關(guān)于在Linux環(huán)境下安裝MySQL8.0的完整步驟,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • MySQL數(shù)據(jù)庫8——數(shù)據(jù)庫中函數(shù)的應(yīng)用詳解

    MySQL數(shù)據(jù)庫8——數(shù)據(jù)庫中函數(shù)的應(yīng)用詳解

    這篇文章主要介紹了MySQL數(shù)據(jù)庫8——數(shù)據(jù)庫中函數(shù)的應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03

最新評論