Latch導致MySQL Crash

沃趣科技發表於2016-05-18



沃趣科技高階資料庫專家  董 紅 禹

一、問題概述

最近我們遇到一個MySQL的問題,分析後很有代表意義,特地寫出來供大家參考。 
出現問題是,資料庫先是被置為只讀,然後過了一段時間,MySQL直接Crash掉了 
發生Crash時MySQL的error日誌中列印了以下內容:

		

  1. ----------
  2. SEMAPHORES
  3. ----------
  4. OS WAIT ARRAY INFO: reservation count 1246555
  5. --Thread 140363572082432 has waited at row0upd.cc line 2354 for 253.00 seconds the semaphore:
  6. X-lock (wait_ex) on RW-latch at 0x7fa949340740 created in file buf0buf.cc line 1069
  7. a writer (thread id 140363572082432) has reserved it in mode wait exclusive
  8. number of readers 1, waiters flag 1, lock_word: ffffffffffffffff
  9. Last time read locked in file btr0sea.cc line 931
  10. Last time write locked in file /export/home/pb2/build/sb_0-17068951-1447697721.44/mysql-5.6.28/storage/innobase/row/row0upd.cc line 2354

根據日誌中我們可以看到,執行緒140363572082432要對記錄上一個X鎖,但是等待0x7fa949340740執行緒的RW-latch的釋放

我們在向下看查詢到如下資訊(涉及到使用者資訊 謂詞就用xxx代替):

		

  1. 173 lock struct(s), heap size 30248, 7925 row lock(s), undo log entries 7924
  2. MySQL thread id 5709783, OS thread handle 0x7fa8f0da7700, query id 92213034 10.23.163.54 citicqyh updating
  3. update TB_DEPARTMENT_INFO set TOTAL_USER=1 where ID='ac84f17e-82d3-4519-a1da-0d5a5a835d44'
  4. ---TRANSACTION 53065242, ACTIVE 313 sec fetching rows, thread declared inside InnoDB 2081
  5. mysql tables in use 2, locked 0
  6. MySQL thread id 5428690, OS thread handle 0x7fa8f0136700, query id 92213061 10.23.163.55 citicqyh Sending data
  7. SELECT COUNT(DISTINCT r.user_id) FROM TB_DEPARTMENT_INFO d left join tb_qy_user_department_ref r on r.department_id = d.id WHERE d.org_id = 'xxx' AND (d.dept_full_name LIKE 'xxx%' or d.dept_full_name = 'xxx
  8. Trx read view will not see trx with id >= xxx, sees < xxx
  9. ......還有很多select語句省略

根據上面資訊我們去資料庫中檢視了這些select語句,發現執行計劃都是全表掃描。 
首先資料庫變成了只讀,最後資料庫crash了,crash輸出的資訊如下:

		

  1. ----------------------------
  2. END OF INNODB MONITOR OUTPUT
  3. ============================
  4. InnoDB: ###### Diagnostic info printed to the standard error stream
  5. InnoDB: Error: semaphore wait has lasted > 600 seconds
  6. InnoDB: We intentionally crash the server, because it appears to be hung.
  7. 7fa8f9580700 InnoDB: Assertion failure in thread 140363714529024 in file srv0srv.cc line 1754
  8. InnoDB: We intentionally generate a memory trap.
  9. InnoDB: Submit a detailed bug report to http://bugs.mysql.com.
  10. InnoDB: If you get repeated assertion failures or crashes, even
  11. InnoDB: immediately after the mysqld startup, there may be
  12. InnoDB: corruption in the InnoDB tablespace. Please refer to
  13. InnoDB: http://dev.mysql.com/doc/refman/5.6/en/forcing-innodb-recovery.html
  14. InnoDB: about forcing recovery.

InnoDB: Error: semaphore wait has lasted > 600 seconds 提示600秒沒有響應 資料庫選擇了Crash 強制重啟

從報錯資訊來看:

  1. update語句需要在記錄上面加X鎖,但是必須等待RW-Latch的釋放
  2. 由於有大量的select語句是全表掃描,一直佔用latch沒有釋放,update遲遲競爭不到RW-latch
  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

對應的程式碼摘錄如下:

		

  1. rw_lock_create(PFS_NOT_INSTRUMENTED, &block->lock, SYNC_LEVEL_VARYING);

跟蹤原始碼,知道這個Latch是MySQL在資料庫啟動,初始化 innodb_buffer_pool時,將Latch建立好的。對應的函式呼叫過·程:

		

  1. buf_pool_init_instance()->buf_chunk_init()->buf_block_init()

正是由於這個RW-Latch被長時間佔用了,其他的執行緒一直競爭不到,才導致了這個問題

三、修復建議

這類問題的發生多數都是因為SQL寫的不好,在表上面進行了大量的全表掃描佔用了大量的Latch,解決方案就是避免SQL長時間佔用latch:

  1. 修改select查詢避免全表掃描,避免latch長期被佔用
  2. 適當的加索引,讓select執行更快,也避免一個select鎖的資料更少
  3. 適當加大buffer pool instance,每個buffer pool都有自己獨立的Latch,避免latch競爭。



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28218939/viewspace-2102421/,如需轉載,請註明出處,否則將追究法律責任。

相關文章