亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > MySQL > 正文

MySQL redo死鎖問題排查及解決過程分析

2024-07-24 13:10:57
字體:
來源:轉載
供稿:網友

問題背景

周一上班,首先向同事了解了一下上周的測試情況,被告知在多實例場景下 MySQL Server hang 住,無法測試下去,原生版本不存在這個問題,而新版本上出現了這個問題,不禁心頭一顫,心中不禁感到奇怪,還好現場環境還在,為排查問題提供了一個好的環境,隨即便投入到緊張的問題排查過程當中。問題實例表現如下:

 

復制代碼 代碼如下:

并發量為 384 的時候出現的問題;
MySQL 服務器無法執行事務相關的語句,即使簡單的 select 語句也無法執行;
所有線程處于等待狀態,無法 KILL。
現場環境的收集

 

首先,通過 pstack 工具獲取當前問題實例的堆棧信息以便后面具體線程的查找 & 問題線程的定位:

MySQL,redo,死鎖

使用 pt-pmp 工具統計 hang.info 中的進程信息,如下:

MySQL,redo,死鎖
MySQL,redo,死鎖
MySQL,redo,死鎖
MySQL,redo,死鎖
MySQL,redo,死鎖
MySQL,redo,死鎖
MySQL,redo,死鎖
MySQL,redo,死鎖
MySQL,redo,死鎖

問題分析

從堆棧上可以看出,有這樣幾類線程:

等待進入 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,死鎖

MySQL,redo,死鎖

MySQL,redo,死鎖

這里我們簡單介紹一下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 的有序性,代碼說明如下:

MySQL,redo,死鎖

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 的地方;

MySQL,redo,死鎖

結合現有 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 的定義如下:

MySQL,redo,死鎖

MySQL,redo,死鎖

由以上的分析可以得出 問題二 的答案:

只有兩個線程和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 的堆棧中,然后查看了函數:

MySQL,redo,死鎖

 

MySQL,redo,死鎖

 

由問題二的分析過程可以得出某線程在 log_flush_order_mutex_exit 的退出過程沒有將 Thread 446 喚醒,那么就順著這個函數找,看它如何喚醒其它本程的,在沒有辦法的時候也只有這樣一步一步的分析代碼,希望有些收獲,隨著函數調用的不斷深入,將目光定在了 mutex_exit_func 上, 函數中的注釋引起了我的注意:

 

MySQL,redo,死鎖

 

MySQL,redo,死鎖

 

從上面的注釋中可以得到兩點信息:

 

由于 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() & 常歸的異常檢查,正好回答了上面的問題,詳細堆棧如下:

MySQL,redo,死鎖
 

 

經過上面的分析問題越來越明朗了,過程可以簡單的歸結為:

 

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 住的整個過程如下:

 

MySQL,redo,死鎖

 

關于 Memory barrier 的介紹可以參考 :

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

 

 

 

問題解決

 

既然知道了問題產生的原因,那么問題也就可以順利解決了,有兩種方法:

 

直接移除 log_get_lsn 在此處的判斷,本身就是開發人員加的一些判斷信息,為了定位 LSN 的異常而寫的,用到的時候也Crash了,用處不大; 保留判斷,將 log_get_lsn 修改為 log_peek_lsn, 后者會首先進行 try_lock,當發現上鎖失敗的時候會直接返回,而不進行判斷,這種方法較優雅些; 經過修改之后的版本在測試過程中沒有沒有再復現此問題。

 

問題擴展

 

雖然問題解決了,但官方版本中肯定存在著這個問題,為什么 buglist 沒有找到相關信息呢,于是在查看了最新代碼,發現這個問題已經修復,修復方法為上面列的第二種方法,詳細的 commit message 信息如下:

 

MySQL,redo,死鎖

bug影響范圍:MySQL 5.6.28 及之前的版本都有此問題。

 

注:相關教程知識閱讀請移步到MYSQL教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久国产成人精品| 亚洲精品aⅴ中文字幕乱码| 精品国产一区二区在线| 亚洲欧美三级在线| 日韩中文字幕精品视频| 精品一区二区三区电影| 亚洲欧美一区二区三区在线| 精品中文字幕久久久久久| 日本19禁啪啪免费观看www| 国产欧美亚洲精品| 亚洲天堂免费视频| 美女啪啪无遮挡免费久久网站| 亚洲成人av资源网| 九九精品在线观看| 亚洲国产精品国自产拍av秋霞| 日韩免费在线看| 97国产精品人人爽人人做| www.美女亚洲精品| 日韩成人性视频| 久久天天躁狠狠躁老女人| 欧美久久精品午夜青青大伊人| 久久久噜噜噜久噜久久| 欧美高清视频一区二区| 欧美大片在线影院| 欧洲亚洲免费在线| 色综合久久久888| 黄色一区二区在线观看| 中文字幕一区电影| 国产精品一久久香蕉国产线看观看| 亚洲美女精品久久| 久久久久久久久久久免费精品| 国产精品免费视频久久久| 国产一区二区日韩精品欧美精品| 欧美综合激情网| 亚洲欧美日本精品| 国产亚洲欧美日韩精品| 久久免费精品日本久久中文字幕| 日韩成人av网址| 国产精品久久久久免费a∨| 美乳少妇欧美精品| 51午夜精品视频| 日韩亚洲成人av在线| 成人免费午夜电影| 久久人91精品久久久久久不卡| 国产福利精品av综合导导航| 欧美成人亚洲成人日韩成人| 欧美大片在线影院| 日韩av免费在线观看| 久久人人爽国产| 国产欧美日韩91| 91av免费观看91av精品在线| 欧美日韩黄色大片| 精品国内亚洲在观看18黄| 国产亚洲精品久久久久久777| www日韩中文字幕在线看| 欧美黄色三级网站| 91av视频导航| 欧美激情亚洲精品| 久久久伊人欧美| 欧美亚洲视频一区二区| 精品国产一区二区三区在线观看| 中文字幕日韩欧美精品在线观看| 国产成人av网址| 国产亚洲aⅴaaaaaa毛片| 欧美又大粗又爽又黄大片视频| 亚洲精品av在线| 中文字幕精品一区二区精品| 国内精品视频一区| 91高清视频免费| 91在线免费观看网站| 亚洲国产91精品在线观看| 国产成人高潮免费观看精品| 亚洲精品一区二区久| 91久久久久久久久久久| 久久国产精品久久久久久久久久| 欧美乱妇高清无乱码| 亚洲理论电影网| 777精品视频| 日韩小视频在线| 日韩视频―中文字幕| 亚洲精品国产美女| 亚洲人精品午夜在线观看| 久久天天躁狠狠躁夜夜av| 亚洲成人网在线观看| 成人免费观看a| 成人精品视频99在线观看免费| 91在线|亚洲| 九九久久久久久久久激情| 精品毛片网大全| 国产日韩在线免费| 色香阁99久久精品久久久| 国内精品视频久久| 91av在线不卡| 国产亚洲成av人片在线观看桃| 欧美日韩精品在线视频| 国产美女精品免费电影| 91国产精品电影| 九九精品视频在线观看| 日韩欧美国产高清91| 亚洲国产精品va在线看黑人| 欧美在线精品免播放器视频| 亚洲自拍偷拍福利| 成人黄色短视频在线观看| 午夜精品一区二区三区在线视频| 性欧美在线看片a免费观看| 国产噜噜噜噜久久久久久久久| 狠狠色狠色综合曰曰| 国产成人在线一区二区| 91久久精品国产91性色| 97精品国产97久久久久久春色| 久久精品99久久香蕉国产色戒| 国产男人精品视频| 成人免费午夜电影| 亚洲精品视频在线播放| 欧美高清在线视频观看不卡| 欧美日韩午夜视频在线观看| 午夜精品蜜臀一区二区三区免费| 一个色综合导航| 91人人爽人人爽人人精88v| 国产精品久久97| 欧美日韩电影在线观看| 日本精品视频网站| 日韩大陆欧美高清视频区| 97久久伊人激情网| 亚洲男人av电影| 黑人巨大精品欧美一区二区| 午夜精品福利在线观看| 亚洲精品丝袜日韩| 亚洲图中文字幕| 亚洲欧美日韩国产中文专区| 国产精品自产拍在线观| 欧美激情一级二级| 成人av资源在线播放| 亚洲一区二区三区sesese| 成人a视频在线观看| 亚洲亚裔videos黑人hd| 91久久精品美女高潮| 精品国产91久久久久久老师| xvideos成人免费中文版| 亚洲一区av在线播放| 亚洲第五色综合网| 亚洲免费av网址| 亚洲男人av电影| 欧美日韩国产成人高清视频| 欧美资源在线观看| www国产亚洲精品久久网站| 亚洲一区二区在线| 青青久久av北条麻妃黑人| 一级做a爰片久久毛片美女图片| 欧美电影在线免费观看网站| 欧美日韩国产一区二区| 亚洲一区二区三区香蕉| 日韩av在线影院| 色系列之999| 国产一区二区三区四区福利| 日韩中文理论片| 亚洲国产福利在线| 亚洲片在线资源| 亚洲欧美激情一区| 国产精品一区二区三区毛片淫片| 精品香蕉一区二区三区| 一本一道久久a久久精品逆3p| 黄色一区二区在线观看| 在线精品91av|