前言html
任何系統無論在什麼階段都須要關注生產環境錯誤日誌,最近幾個月內,發現偶爾會出現數據庫死鎖狀況。之前碰到的數據庫類錯誤大部分是SQL語法形成的錯誤,來到新東家以後才第一次碰到死鎖狀況,之前是搞遊戲開發,如今是搞電商類開發,多是不一樣的項目不一樣的業務的緣由吧,查閱了各類資料後發現,是我想錯了:(。通常業務瓶頸在數據庫層,對於數據庫層的問題須要重點關注,覺得死鎖這種狀況是很嚴重的問題,這個要分狀況,偶爾死鎖對業務不會有太大的影響,我又想錯了:(。mysql
蟲子發現
第一次發現死鎖很驚訝,這個是什麼鬼?不知道是什麼緣由?不知道怎麼解決?不知道會不會影響業務?沒有搞清楚這個問題,生怕是業務的定時炸彈,天天內心不踏實。蟲子發現以下:sql
死鎖
對數據庫記錄操做以前,當前線程須要先請求得到相關鎖,得到鎖以後纔會執行SQL語句。鎖根據粒度分爲表鎖和行鎖(不是全部存儲引擎都支持行鎖),鎖根據類型分爲排他鎖和共享鎖(不一樣的操做須要得到的鎖類型不一樣,不一樣的鎖類型行爲也不相同)。資源越少,出現死鎖的機率會更小,好比只支持表鎖的存儲引擎會比支持行鎖的存儲引擎出現的機率更小。
併發事務出現時,當出現多個事務間彼此等待對方資源釋放,事務因沒有處理完,已經得到鎖的資源不會釋放,你們彼此這樣等待着僵持着,這樣會一直下去,死鎖就這樣產生了。
死鎖現象關係型數據庫沒法避免,不是MySQL 數據庫獨有。MySQL 數據庫會自動檢查死鎖狀況,當發現時,會回滾更簡單的事務並返回給線程一個錯誤。怎麼判斷哪一個事務更簡單呢,根據事務影響的紀錄行數判斷,紀錄行數越小被認爲更簡單。數據庫
診斷分析
當出現死鎖時,爲了解決這個問題,須要診斷分析當時出現死鎖的上下文信息以及相關的執行語句,這樣才能知道怎麼避免。MySQL 數據庫只保存最近一次的死鎖事務,若是同時有超過2個事務出現死鎖,至多隻保存2個事務信息。執行MySQL 客戶端命令:SHOW ENGINE INNODB STATUS得到最近一次事務死鎖相關信息。若是是MySQL 5.6以後的版本,能夠打開全局變量innodb_print_all_deadlocks=ON,這樣死鎖相關信息會保存到MySQL 錯誤日誌中。若是是不支持這個變量的版本,能夠採用定時執行客戶端命令採集死鎖信息,而後保存到日誌文件中,每一個事務都有惟一編號,能夠根據這個去重。當死鎖頻繁出現時,須要引發注意;當偶爾出現時,能夠不用關注。死鎖信息格式以下,不一樣MySQL 版本內容會有點差別,相關說明能夠查看參考資料一。併發
預防措施
知道死鎖爲什麼物,以及經過分析診斷日誌信息,就能夠對症下藥了,可是有一些通用的基本原則能夠遵照:
1 儘量保持事務簡單以及快速執行;
2 儘量保持事務影響行數少以及涉及的相關表少;
3 避免使用外鍵約束,其實大部分應用容許數據冗餘的;
4 影響行數大的事務儘可能使用相同的排序過濾;
5 數據庫併發不高的業務,能夠經過某種方式順序執行語句,一次只執行一個,經過查資料知道一種實現方式:建立一個表,這個表始終只有一條紀錄,各個事務經過爭奪這個鎖來實現線性執行,感受這個應該沒啥用:(;
6 有些場景死鎖會檢查不到,設置鎖的過時時間就很重要了,MySQL能夠經過設置選項innodb_lock_wait_timeout;spa
危害程度
鎖資源得不到及時釋放,會影響數據庫併發處理,若是常常出現死鎖,須要及時採起措施處理,若是隻是偶爾出現,業務邏輯捕捉到死鎖錯誤能夠採起重試執行事務,對業務不會有什麼影響,總之具體問題須要具體分析:)線程
後記
一開始遇到這個問題有點緊張,覺得是很嚴重的問題,無從下手,不瞭解相關的背景知識嘛,看來人醜仍是要多讀書啊(:(:3d
參考資料
【1】How to deal with MySQL deadlocks
https://www.percona.com/blog/2014/10/28/how-to-deal-with-mysql-deadlocks/
【2】deadlock
http://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_deadlock
【3】Deadlock Detection and Rollback
http://dev.mysql.com/doc/refman/5.7/en/innodb-deadlock-detection.html
【4】 How to Cope with Deadlocks
http://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks.html日誌