程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> MySQL redo逝世鎖成績排查及處理進程剖析

MySQL redo逝世鎖成績排查及處理進程剖析

編輯:MySQL綜合教程

MySQL redo逝世鎖成績排查及處理進程剖析。本站提示廣大學習愛好者:(MySQL redo逝世鎖成績排查及處理進程剖析)文章只能為提供參考,不一定能成為您想要的結果。以下是MySQL redo逝世鎖成績排查及處理進程剖析正文


成績配景

周一下班,起首向同事懂得了一下上周的測試情形,原告知在多實例場景下 MySQL Server hang 住,沒法測試下去,原生版本不存在這個成績,而新版本上湧現了這個成績,不由心頭一顫,心中不由覺得奇異,還好現場情況還在,為排盤問題供給了一個好的情況,隨即使投入到重要的成績排查進程傍邊。成績實例表示以下:


並發量為 384 的時刻湧現的成績;
MySQL 辦事器沒法履行事務相干的語句,即便簡略的 select 語句也沒法履行;
一切線程處於期待狀況,沒法 KILL。
現場情況的搜集

起首,經由過程 pstack 對象獲得以後成績實例的客棧信息以便前面詳細線程的查找 & 成績線程的定位:


應用 pt-pmp 對象統計 hang.info 中的過程信息,以下:










成績剖析

從客棧上可以看出,有如許幾類線程:

期待進入 INNODB engine 層的用戶線程,測試情況中 innodb_thread_concurrency=16, 當 INNODB 層中的活潑線程數量年夜於此值時則須要列隊,所以會有年夜量的列隊線程,這個參數的影響&感化自己就是一篇很不錯的文章,因為篇幅無限,在此不做擴大,感興致者可以參考官方文檔:14.14 InnoDB Startup Options and System Variables;
操作進程中須要寫 redo log 的後台線程,重要包含 page cleaner 線程、異步 io threads等;
正在讀取Page頁面的 purge 線程 & 操作 change buffer 的 master thread;
年夜量的須要寫 redo log 的用戶線程。
從以上的分類不好看出,一切須要寫 redo log 的線程都在期待 log_sys->mutex,那末這個掩護 redo log buffer 的 mutex 被畢竟被哪一個線程獲得了呢,是以,我們可以順著這個線索停止成績排查,須要處理以下成績:

成績一:哪一個線程獲得了 log_sys->mutex ?
成績二:獲得 log_sys->mutex 的線程為何沒有持續履行下去,是在等其它鎖照樣其它緣由?
成績三:假如不是硬件成績,全部資本竟爭的進程是若何的?

1.成績一:由表及裡

在查找 log_sys->mutex 所屬線程情形時,有兩點可以贊助我們疾速的定位到這個線程:

因為 log_sys->mutex 同時只能被統一個線程取得,所以在 pt-pmp 的信息輸入中便可以消除線程數量年夜於1的線程;
此線程既然曾經獲得了 log_sys->mutex, 那就應當照樣在寫日記的進程中,是以重點可以檢查寫日記的邏輯,即包含:mtr_log_reserve_and_write 或 log_write_up_to 的客棧。
順著下面的思緒很快的從 pstack 中找到了以下線程:

這裡我們簡略引見一下MySQL寫 redo log 的進程(省略undo & buffer pool 部門),當對數據停止修正時,MySQL 會起首對針對操作類型記載分歧的 redo 日記,重要進程是:

記載操作前的數據,依據分歧的類型生成分歧的 redo 日記,redo 的類型可以參考文件:src/storage/innobase/include/mtr0mtr.h 記載操作以後的數據,關於分歧的類型會包括分歧的內容,詳細可以參考函數:recv_parse_or_apply_log_rec_body(); 寫日記到 redo buffer,並將此次觸及到髒頁的數據參加到 buffer_pool 的 flush list 鏈表中; 依據 innodb_flush_log_at_trx_commit 的值來斷定在commit 的時刻能否停止 sync 操作。

下面的客棧則是寫Redo後將髒頁加到 flush list 進程中時 hang 住了,即此線程在獲得了 log_sys->mutex 後,在獲得 log_sys->log_flush_order_mutex 的進程中 hang 住了,而此時有年夜量的線程在期待該線程釋放log_sys->mutex鎖,成績一 曾經有了謎底,那末log_sys->log_flush_order_mutex 是個甚麼東東,它又被哪一個占用了呢?

解釋:

1、MySQL 的 buffer pool 保護了一個有序的髒頁鏈表 (flush list according LSN order),如許在做 checkpoint & log_free_check 的進程中可以很快的定位到 redo log 須要推動的地位,在將髒頁參加;
2、flush list 進程中須要對其上鎖以包管 flush list 中 LSN 的有序性, 然則假如應用 log_sys->mutex,在並發量年夜的時刻則會形成 log_sys->mutex 的 contention,進而惹起機能成績,是以添加了別的一個 mutex 來掩護髒頁按 LSN 的有序性,代碼解釋以下:


2.成績二:彈盡糧絕

在成績一的排查進程中我們肯定了 log_sys->mutex 的所屬線程, 這個線程在取得 log_sys->log_flush_order_mutex 的進程中 hang 住了,是以線程客棧可以分認為下幾類:

Thread 446, 取得 log_sys->mutex, 期待獲得 log_sys->log_flush_order_mutex 以把髒頁參加到 buffer_pool 的 flush list中; 須要取得 log_sys->mutex 以寫日記或許讀取日記信息的線程; 未知線程取得 log_sys->log_flush_order_mutex,在做其它工作的時刻被 hang 住。


是以,成績的症結是找到哪一個線程獲得了 log_sys->log_flush_order_mutex。

為了找到相干的線程做了以下操作:

查找獲得 log_sys->log_flush_order_mutex 的處所;


聯合現有 pstack 中的線程信息,細心檢查上述查找成果中的相干代碼,發明根本沒有線程取得 log_sys->log_flush_order_mutex; gdb 進入 MySQL Server, 將 log_sys->log_flush_order_mutex 打印出來,發明 {waiters=1; lock_word= 0}!!!,即 Thread 446 在期待一個余暇的 mutex,而這個Mutex也確切被期待,因為我們的版本為 Release 版本,所以許多有效的信息沒有方法獲得,而若用 debug 版本跑則很難重現成績,log_flush_order_mutex 的界說以下:

由以上的剖析可以得出 成績二 的謎底:

只要兩個線程和log_sys->log_flush_order_mutex有關,個中一個是 Thread 446 線程, 別的一個則是比來一次挪用 log_flush_order_mutex_exit() 的線程; 現有線程中某個線程在釋放log_sys->log_flush_order_mutex的進程中沒有叫醒 Thread 446,招致Thread 446 hang 並形成其它線程不克不及取得 log_sys->mutex,進而形成實例弗成用; log_sys->log_flush_order_mutex 沒有被任何線程取得。 3.成績三:逢凶化吉

由成績二的剖析進程可知 log_sys->log_flush_order_mutex 沒有被任何線程取得,可是為何 Thread 446 沒有被叫醒呢,旌旗燈號喪失照樣法式成績?假如是旌旗燈號喪失,為何可以穩固復現?官方的bug list 列表中是沒有相似的 Bug的,搜了一下社區,發明可用信息很少,這個時刻剖析似乎墮入了逝世胡同,心裡壓力開端有形中變年夜……似乎沒有方法,然則任何成績都是有緣由的,找到了緣由,也就是有解的了……再一次將留意力移到了 Thread 446 的客棧中,然後檢查了函數:

由成績二的剖析進程可以得出某線程在 log_flush_order_mutex_exit 的加入進程未將 Thread 446 叫醒,那末就順著這個函數找,看它若何叫醒其它本程的,在沒有方法的時刻也只要如許一步一步的剖析代碼,願望有些收成,跟著函數挪用的赓續深刻,將眼光定在了 mutex_exit_func 上, 函數中的正文惹起了我的留意:

從下面的正文中可以獲得兩點信息:

因為 memory barrier 的存在,mutex_get_waiters & mutex_reset_lock_word 的挪用次序能夠與履行次序相反,這類情形下會惹起 hang 成績; 專門寫了一個函數 sync_arr_wake_threads_if_sema_free() 來處理上述成績。

由下面的正文可以看到,其實不是旌旗燈號喪失,而是多線程 memory barrier 的存在能夠會形成指令履行的次序的異常,這類成績肯定存在,但既然有sync_arr_wake_threads_if_sema_free() 躲避這個成績,為何還會存在 hang 呢?有了這個線索,剎時感到有了些盼頭……經由查找 sync_arr_wake_threads_if_sema_free 只在 srv_error_monitor_thread 有挪用,這個線程是專門對 MySQL 外部異常情形停止監控並打印出 error 信息的線程,污名昭著的 600S 他殺案也是它的佳構, 那末成績來了:

機械周末都在 hang 著,為何沒有檢測到異常並 abort 呢? 既然 sync_arr_wake_threads_if_sema_free 可以叫醒,為何沒有叫醒呢?

順著這個思緒,檢查了pstack 中 srv_error_monitor_thread 的客棧,可以發明此線程在獲得 log_sys->mutex 的時刻hang 住了,是以沒法履行sync_arr_wake_threads_if_sema_free() & 常歸的異常檢討,正好答復了下面的成績,具體客棧以下:



經由下面的剖析成績愈來愈晴明了,進程可以簡略的歸結為:

Thread 446 取得 log_sys->mutex, 然則在期待 log_sys->log_flush_order_mutex 的進程中沒有被叫醒; Thread XXX 在釋放 log_sys->log_flush_order_mutex 的進程中湧現了 memory barrier 成績,沒有叫醒 Thread 446; Thread 470 取得 log_sys->mutex 時被 hang 住,招致沒法履行 sync_arr_wake_threads_if_sema_free(), 招致了全部實例的 hang 住; Thread 470 須要取得 Thread 446 的 log_sys->mutex, 而 Thread 446 須要被 Thread 470 叫醒才會釋放 log_sys->mutex;

聯合 log_sys->log_flush_order_mutex 的狀況信息,實例 hang 住的全部進程以下:


關於 Memory barrier 的引見可以參考 :

Memory barrierhttp://name5566.com/4535.html

成績處理

既然曉得了成績發生的緣由,那末成績也便可以順遂處理了,有兩種辦法:

直接移除 log_get_lsn 在此處的斷定,自己就是開辟人員加的一些斷定信息,為了定位 LSN 的異常而寫的,用到的時刻也Crash了,用途不年夜; 保存斷定,將 log_get_lsn 修正為 log_peek_lsn, 後者會起首停止 try_lock,當發明上鎖掉敗的時刻會直接前往,而不停止斷定,這類辦法較優雅些; 經由修正以後的版本在測試進程中沒有無再復現此成績。

成績擴大

固然成績處理了,但官方版本中確定存在著這個成績,為何 buglist 沒有找到相干信息呢,因而在檢查了最新代碼,發明這個成績曾經修復,修復辦法為下面列的第二種辦法,具體的 commit message 信息以下:


bug影響規模:MySQL 5.6.28 及之前的版本都有此成績。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved