回顧一次“神奇”的MySQL死鎖問題

問題背景

大約在今年5月份左右饰迹,由于系統(tǒng)同時需要訪問兩個MySQL集群的數(shù)據(jù)艰山,決定使用MyCat作為數(shù)據(jù)庫中間件,因此在一個夜黑風高的晚上勤庐,屏蔽了用戶訪問,準備將系統(tǒng)切換成MyCat好港。整個切換過程其實很簡單愉镰,配置好MyCat配置文件,修改應用的數(shù)據(jù)源訪問連接即可大功告成钧汹。但是用過MyCat的人應該都清楚丈探,MyCat有個很蛋疼的毛病,必須要求MySQL開啟大小寫忽略拔莱,否則啟動后找不到表名大寫的表碗降。不巧的是公司之前DBA要求所有的表名、字段名都必須大寫(OracleDBA塘秦,完全忽略了MySQL的感受)讼渊。沒轍,只能修改表名了尊剔,將大寫全部轉(zhuǎn)換為小寫爪幻。通過一個存儲過程循環(huán)游標構建ALTER TABLE tableName RENAME newName語句來將表名改為大寫。本以為執(zhí)行應該會很快結束,但是整個命令持續(xù)了半個小時挨稿,卡在一張表上不動了搔预,執(zhí)行SHOW ENGINE INNODB STATUS,發(fā)現(xiàn)已經(jīng)出現(xiàn)死鎖叶组。

疑問

這里先來介紹一下ALTER TABLE tableName RENAME newName命令,這一個名副其實的DDL操作历造,但是整個命令的執(zhí)行僅僅是變更INFORMATION_SCHEMA庫中相應字典表的數(shù)據(jù)(例如表名)甩十,且此時沒有任何對表的操作,怎么會導致死鎖問題呢吭产?

毫無思路侣监,但是仔細想想,這個操作應該會修改磁盤上.ibd臣淤、.frm文件的名稱橄霉,否則僅僅是字典表變了也找不到對應的文件。如果說修改文件的話邑蒋,就可能會出現(xiàn)死鎖問題姓蜂,若此時文件被其他進程讀寫占用呢,那就有可能會造成死鎖医吊。

究竟是不是這樣呢钱慢,由于本人對MySQL底層了解的不深,只好求助論壇了卿堂。(以下內(nèi)容摘自論壇)

探索

InnoDB buffer pool中的page管理牽涉到兩個鏈表束莫,一個是lru鏈表,一個是flush 臟塊鏈表草描,由于數(shù)據(jù)庫的特性:

1.臟塊的刷新览绿,是異步操作;

2.page存在兩個版本穗慕,一個是ibd文件的持久化版本饿敲,和buffer pool內(nèi)存中的當前版本。

所以在對table對象進行ddl變更的時候逛绵,要維護兩個版本之間的一致性诀蓉,有一些操作需要同步進行page緩存的管理。例如以下三種ddl操作:

1. flush table t for export

這是MySQL 5.6提供的InnoDB transportable tablespace功能暑脆,用于在不同實例之間進行表傳輸渠啤。由于需要透明的在物理層面遷移ibd文件,所以需要保證buffer pool中的page和ibd文件中的page的一致性添吗。其操作步驟如下:

持有t表的MDL鎖沥曹,保證在t表上沒有活躍事務,即buffer pool中的臟page都是已提交事務;

掃描buffer pool中的flush list妓美,同步刷下臟塊僵腺;

記錄數(shù)據(jù)字典信息到cfg文件,用于目標端的表結構匹配和驗證壶栋,最后在目標端import的時候辰如,變更page的space,max_lsn等贵试。

2. drop table t

在對表進行刪除的時候琉兜,需要清理掉buffer pool中的page毙玻,但如果表比較大,占用過多的buffer pool桑滩,清理的動作會影響到在線的業(yè)務,所以MySQL提供了lazy drop table的方式运准。

同步方式: 掃描lru鏈表幌氮,如果page屬于t表,就從lru鏈表胁澳,hash表, flush list中刪除慢洋,回收block到free list中。

lazy方式: 掃描lru鏈表陆盘,如果page屬于t表普筹,就給page設置一個space_was_being_deleted屬性,等lru置換或者checkpoint flush dirty block的時候進行清理隘马。

3. alter table t rename to t1

rename table name操作,雖然是DDL酸员,但rename操作只是變更了數(shù)據(jù)字典中的table name和文件系統(tǒng)的ibd文件名稱,所以酿愧,在rename的過程中邀泉,不存在對buffer pool中屬于t表的page的同步操作嬉挡,但由于要變更表名,即需要同步對文件的IO操作庞钢。

問題現(xiàn)象:

在MySQL 5.5版本上,error日志大量報出以下的錯誤信息:


查看操作日志颜懊,是一個普通的rename語句操作风皿,但持續(xù)很久,因為rename只是數(shù)據(jù)字典的變更揪阶,除了MDL鎖阻塞以外

不應該持續(xù)這么長時間患朱,pstack查看線程棧信息:



這里我只列了有意義的三個線程:

用戶線程Thread 5

用戶線程確實在進行rename操作裁厅,但阻塞在fil_rename_tablespace函數(shù)中。

master線程Thread 120

InnoDB的master線程阻塞在fil_mutex_enter_and_prepare_for_io函數(shù)中执虹。

IO線程Thread 100

InnoDB的IO線程一共有8個,4個讀侥啤,4個寫線程,發(fā)現(xiàn)都在os_event_wait_low中茬故,也就是都空閑著等待condition中盖灸。

從上面的調(diào)用棧來看,線程之間長時間維持在這種狀態(tài)下磺芭,明顯發(fā)生了死鎖,在我們解這個死鎖之前钾腺,我們先來回顧一點背景知識,然后再說明死鎖的真正原因姻报。

InnoDB背景

checkpoint

由于對數(shù)據(jù)庫的數(shù)據(jù)操作也遵循read-update-write的方式间螟,所以數(shù)據(jù)的更新剧辐,會把buffer pool中的page變成臟塊邮府,由于write-ahead logs機制保證事務的完整性,臟塊的write可以變成異步的褂傀,但又由于buffer pool的大小終究有限,而且對于recovery的時間的要求同波,又要求臟塊的flush又要持續(xù)保證叠国。

MySQL 5.5的版本由master thread來承擔dirty flush的角色, dirty flush的過程就稱為making checkpoint粟焊,lsn的推進保證了recovery的時間不被持續(xù)的變長。刷新的策略悲雳,受到當前IO pending的情況香追,double write-buffer是否打開,buffer pool中dirty page所占的比例透典,以及innodb_max_dirty_pages_pct參數(shù)的設置,進行靈活刷新峭咒,具體的代碼細節(jié),這里就不展開了钙皮。

異步IO

由于dirty flush是異步的顽决,所以,master thread只負責提交IO請求才菠,真正的IO操作是由IO helper thread來完成的。InnoDB使用的simulate AIO和native AIO會有一些差別可都,我們這里以simulate AIO為例進行說明。假設double write-buffer是打開的:

首先master thread搜集dirty pages旋炒,同步寫入double write-buffer;

由于double write-buffer的方式是buffered write瘫镇,所以等double write-buffer寫滿了之后答姥;

同步把double write-buffer的page順序?qū)懭氲絠bdata系統(tǒng)表空間中,如果完成之后系統(tǒng)crash鹦付,可以使用持久化的double write-buffer進行page恢復;

開始把 double write-buffer中的page郎嫁,寫入真正的ibd文件中潘明。依次提交異步IO操作秕噪,提交IO操作的步驟分為:

持有fil_system mutex,判斷當前tablespace是否可用腌巾,

判斷當前fil_space的stop_io標示,如果設置就循環(huán)等待

如果stop_io沒有標示吓坚,就打開fil_space對應的ibd文件句柄灯荧,然后遞增 fil_space->n_pending

提交IO請求

等double write-buffer中的pages提交完所有的IO請求,使用os_aio_simulated_wake_handler_threads來喚醒IO helper thread來完成IO操作逗载。

Rename 操作

接下來我們來看下rename操作的步驟:

首先在server層hold MDL鎖;

進入InnoDB層挚躯,首先使用自治事務變更數(shù)據(jù)字典擦秽,包括SYS_TABLES漩勤,SYS_FOREIGN缩搅;

變更數(shù)據(jù)字典的內(nèi)存對象,包括table, index, foreign list等誉己;

變更fil_space對象以及對應的ibd數(shù)據(jù)文件名稱,其中變更文件系統(tǒng)名稱的時候:

設置當前的fil_space的stop_io噪猾,阻止再進行IO操作

判斷當前是否有IO pending筑累,如果有,就等IO pending結束

如果沒有IO pending慢宗,就關閉opened的句柄,并rename文件名稱

恢復stop_io標示

提交自治事務敏晤。

有了這些操作的具體步驟缅茉,我們就可以清晰的分析出死鎖的原因。

死鎖原因

兩個線程蔬墩,一個是master thread,需要提交flush dirty block的異步IO請求奏司;一個是user thread樟插,需要進行rename操作。

Rename操作黄锤,只變更數(shù)據(jù)字典和ibd文件名,并不需要同步buffer pool中的page勉吻,唯一需要同步的就是IO操作旅赢,通俗一點說惑惶,也就是在user thread進行rename table需要變更ibd文件名的時候短纵,其它線程暫時不要對這個文件進行IO操作,等rename完成后香到,可以重新打開這個ibd文件,接著進行IO操作千绪。

InnoDB使用兩個標識來進行IO同步操作梗脾,即stop_io,n_pending炸茧。

stop_io:user thread要進行rename操作,提前設置這個標識辕狰,表示IO操作可以先hold暫停控漠。

n_pending:master thread要進行flush操作,我已經(jīng)提交了IO請求柬脸,user thread要進行rename可以先hold毙驯,等IO完成。

假設下面的時序:

master thread提交了1個IO請求爆价,設置了n_pending媳搪;

rename操作設置stop_io,判斷n_pending>0 就等待序愚;

master thread需要提交剩下的幾個IO等限,發(fā)現(xiàn)stop_io已設置芬膝,就等待形娇;

由于master thread沒有提交完這批IO,沒有喚醒IO helper thread桐早,導致第1個IO請求無法完成,n_pending一直等于1友存;

rename操作因為n_pending一直等于1陶衅,陷入了死等;

master thread發(fā)現(xiàn)stop_io等于true万哪,陷入了死等。

具體的代碼可以參考:



修復方法

修復的方法也比較簡單吟策,在fil_rename_tablespace的時候的止,如果發(fā)現(xiàn)node->n_pending > 0的時候诅福,在sleep之前匾委,發(fā)起一次喚醒動作氓润,即os_aio_simulated_wake_handler_threads,IO helper thread去完成master thread已經(jīng)提交的IO請求挨措,這樣n_pending就會降到0崩溪,死鎖就解開了。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末觉既,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子岭参,更是在濱河造成了極大的恐慌尝艘,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件背亥,死亡現(xiàn)場離奇詭異,居然都是意外死亡娄徊,警方通過查閱死者的電腦和手機盾戴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來橄仆,“玉大人衅斩,你說我怎么就攤上這事∥钒穑” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵宪巨,是天一觀的道長溜畅。 經(jīng)常有香客問我,道長天吓,這世上最難降的妖魔是什么峦椰? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任汰规,我火速辦了婚禮,結果婚禮上滔金,老公的妹妹穿的比我還像新娘。我一直安慰自己餐茵,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布锣笨。 她就那樣靜靜地躺著道批,像睡著了一般。 火紅的嫁衣襯著肌膚如雪椭岩。 梳的紋絲不亂的頭發(fā)上璃赡,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音碉考,去河邊找鬼。 笑死夺荒,一個胖子當著我的面吹牛良蒸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嫩痰,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼串纺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纺棺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤茅撞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后剑令,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡吁津,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年腺毫,在試婚紗的時候發(fā)現(xiàn)自己被綠了挣柬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡急黎,死狀恐怖侧到,靈堂內(nèi)的尸體忽然破棺而出勃教,到底是詐尸還是另有隱情匠抗,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布绳军,位于F島的核電站矢腻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏多柑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一聂沙、第九天 我趴在偏房一處隱蔽的房頂上張望初嘹。 院中可真熱鬧,春花似錦豁生、人聲如沸漫贞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至豌骏,卻和暖如春隐锭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钦睡。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洒琢,地道東北人褐桌。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像荧嵌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子恋技,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容