Mysql 百問系列:死鎖是怎麼發生的

本文主要從共享鎖(S鎖)和獨佔鎖(X鎖)出發,詳細說明兩種鎖的加鎖機制,以及死鎖如何產生。mysql

問題:

  1. 什麼是共享鎖,什麼是獨佔鎖?
  2. 死鎖怎麼發生的?
  3. 發生死鎖了,Mysql 如何處理的?

共享鎖和獨佔鎖

上一篇文章中咱們已經講解了共享鎖和獨佔鎖的基本概念,我這邊再詳細將一下。sql

  • 共享鎖 Shared Locks (S 鎖)  從名字就能夠看出來它容許共享,共享的意思是當你給某個事物加上鎖後,其餘人還能夠爲它加上(僅限)共享鎖。
  • 獨佔鎖 Exclusive Locks (X 鎖), 若是想要修改某個事物,必須先加上X鎖,可是若是該事物已經錯在了S鎖或者X鎖就必須等已經存在的鎖釋放了才能再加上去。
S鎖 X鎖
S鎖 兼容 不兼容
X鎖 不兼容 不兼容

瞭解了S鎖和X鎖,那麼咱們來看看什麼是死鎖吧。
讓咱們來複現下死鎖的過程,以便更好理解。建議能夠根據本身理解來複現死鎖過程,並分析緣由加深理解。否則變成:
眼睛: 我會了。
腦子: 不,你不會。數據庫

準備工做

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `age` int(11) NOT NULL,
  `addresss` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT student VALUES (1,'張一',12,'ssss'),(2,'張二',12,'ssss'),(3,'張三',12,'ssss'),(4,'張四',12,'ssss'),(5,'張五',12,'ssss');
複製代碼

準備工做完成,創建表,並插入5條數據。併發

模擬死鎖第一步
新建一個數據庫鏈接並執行:高併發

###事務A
begin;
select * from student where id in (1,2) lock in share mode;
複製代碼

步驟一 事務A 獲得了id 爲1,2的S鎖。
再新建一個數據庫鏈接並執行:ui

###事務B
begin;
select * from student where id in (1,3) lock in share mode;
複製代碼

步驟二 這時事務B 獲得了id爲1,3的S鎖
而後咱們在事務B 中嘗試獲取數據id = 2 的X鎖spa

事務B
update student set age  =100 where id = 2
複製代碼

步驟三, 因爲id爲2 被事務A加了S鎖,因此事務B 被阻塞。
最後咱們在事務A中嘗試獲取數據id = 3 的X鎖rest

事務A
update student set age  =100 where id = 3

#執行結果:1213 - Deadlock found when trying to get lock; try restarting transaction, Time: 0.005000s
複製代碼

步驟四,因爲id爲3 被事務B加了S鎖,因此事務A被阻塞。 固然其實執行的時候被跳出死鎖警告。事務A回滾撤銷了。code

到此,咱們模擬了整個死鎖過程,因爲B 爲了得到X鎖須要等A執行完成,而A後續反而爲了得到X鎖須要等B執行完成。因此形成了死鎖。事務

死鎖的緣由

從上面的例子中咱們能夠看到死鎖的緣由是相互尋求各自手上的資源(即鎖)致使的。
可是其中有個重要的特性須要說明就是二階段鎖協議。 
二階段鎖協議很簡單:

  1. 須要查詢或者修改時才加鎖。
  2. 事務提交的時候才釋放鎖。

上面例子中步驟三,B之因此會被阻塞,是由於事務A雖然讀取數據結束了,可是事務還沒結束,因此致使讀取是加的鎖沒有釋放。
也是因爲二階段鎖協議的緣由,因此咱們應該儘量避免長事務。由於`事務執行時間越長,加鎖的時間越長,發生死鎖的機率越大```

死鎖如何處理

mysql 能夠設置兩種處理方式:

  1. 第一種就是上面例子鎖展現的,發生死鎖時,自動回滾其中一個事務,解除死鎖。 能夠經過innodb_deadlock_detect 參數設置爲on 開啓。默認狀況下就是on 狀態。
  2. 設置超時時間,也就是發生死鎖時先等待一段時間後,再退出。能夠設置innodb_lock_wait_timeout 設置超時時間。

通常狀況下,咱們會用第一種方式。

留個問題

現實場景中有哪些容易發生死鎖的場景呢?其中最多見的一個例子是減庫存了。
扣減庫存咱們通常有如下步驟

  1. 查看現有庫存是否充足
  2. 庫存充足則減去庫存。 不足則返回扣除失敗。

假設流程的sql語句以下

# 查詢剩餘庫存
select last_number from stock where  id = 1000 lock in share mode;

#庫存足夠 --減去庫存
update stock set last_number = last_number - 1 where id = 1000;
複製代碼

那麼問題是:

  1. 當併發比較高的時候,上面這個業務功能會不會發生死鎖,分析下發生死鎖的緣由?
  2. 爲何要用lock in share mode 加鎖,不加鎖行不行? 高併發下,若是不加鎖會發生什麼問題?
  3. 怎麼修改才能不發生死鎖,又能避免其餘問題?

問題答案下篇文章回答。有收穫,請關注下吧。

相關文章
相關標籤/搜索