問題概述數據庫
最近咱們遇到一個MySQL的問題,分析後頗有表明意義,特意寫出來供你們參考。出現問題是,數據庫先是被置爲只讀,而後過了一段時間,MySQL直接Crash掉了,發生Crash時MySQL的error日誌中打印瞭如下內容:緩存
根據日誌中咱們能夠看到,線程140363572082432要對記錄上一個X鎖,可是等待0x7fa949340740線程的RW-Latch的釋放。markdown
咱們再向下看查詢到以下信息(涉及到用戶信息 謂詞就用xxx代替):併發
根據上面信息咱們去數據庫中查看了這些select語句,發現執行計劃都是全表掃描。首先數據庫變成了只讀,最後數據庫Crash了,Crash輸出的信息以下:函數
InnoDB: Error: semaphore wait has lasted > 600 seconds 提示600秒沒有響應 數據庫選擇了Crash 強制重啓。從報錯信息來看:線程
1,update語句須要在記錄上面加X鎖,可是必須等待RW-Latch的釋放日誌
2,因爲有大量的select語句是全表掃描,一直佔用Latch沒有釋放,update遲遲競爭不到RW-latchblog
3,Innodb 的Diagnostic線程檢查到RW-Latch等待超過了600秒尚未返回,認爲系統出現了嚴重問題,因而觸發了MySQL服務的Crash。索引
進一步分析內存
這裏首先須要補充一下Latch的概念:Latch在MySQL中是用於保護高速緩衝區中共享數據的.
舉個例子:
當咱們執行select時,數據是緩存在buffer pool中的,多個線程併發訪問或者修改這個數據必然須要一個併發控制機制,這個就是Latch
你們知道,數據庫要訪問的數據都必須先存在緩存中,而緩存通常比磁盤空間要小,數據緩存使用hash表來記錄數據頁是否在內存中。在Oracle中的併發控制比較精細:首先會對hash桶加latch,並根據hash桶查找對應的數據並加上pin,而後釋放Latch。而MySQL相對沒有控制得這麼精細,對應的RW-Latch在errlog中說的很清楚,該RW-Latch是在buf0buf.cc的1069行建立的 RW-latch at 0x7fa949340740 created in file buf0buf.cc line 1069
對應的代碼摘錄以下:
跟蹤源碼,知道這個Latch是MySQL在數據庫啓動,初始化 innodb_buffer_pool時,將Latch建立好的。對應的函數調用過程:
正是因爲這個RW-Latch被長時間佔用了,其餘的線程一直競爭不到,才致使了這個問題。
修復建議
這類問題的發生多數都是由於SQL寫的很差,在表上面進行了大量的全表掃描佔用了大量的Latch,解決方案就是避免SQL長時間佔用Latch:
1,修改select查詢避免全表掃描,避免Latch長期被佔用。
2,適當的加索引,讓select執行更快,也避免一個select鎖的數據更少。
3,適當加大buffer pool instance,每一個buffer pool都有本身獨立的Latch,避免latch競爭。