Flink CDC 引起的 MySQL 元數(shù)據(jù)鎖
記一次Flink CDC引起的MySQL元數(shù)據(jù)鎖事故,總結(jié)經(jīng)驗(yàn)教訓(xùn)。后續(xù)在編寫(xiě)Flink CDC任務(wù)時(shí),要處理好異常,避免產(chǎn)生長(zhǎng)時(shí)間的元數(shù)據(jù)鎖。同時(shí)出現(xiàn)生產(chǎn)問(wèn)題時(shí)要及時(shí)排查,不能抱有僥幸心理。
一、事件經(jīng)過(guò)
某天上午,收到系統(tǒng)的告警信息,告警提示:同步MySQL的某張表數(shù)據(jù)到Elasticsearch異常,提示連不上Mysql,當(dāng)時(shí)沒(méi)有太上心,以為可能是偶爾網(wǎng)絡(luò)異常。
然后立馬大量用戶開(kāi)始投訴系統(tǒng)使用有問(wèn)題,同時(shí)聽(tīng)到有同事反饋內(nèi)部系統(tǒng)數(shù)據(jù)導(dǎo)不出來(lái)。此時(shí)我慌了。
立馬看了微服務(wù)網(wǎng)關(guān)、用戶中心服務(wù)、部分流量比較大的BFF層服務(wù),CPU、內(nèi)存、磁盤等都是正常的。但是Pod出現(xiàn)了健康檢查失敗的情況。
于是又趕緊看了日志,出現(xiàn)了大量拿不到MySQL Connection異常。
又趕緊看了MySQL情況,CPU、內(nèi)存、磁盤都是正常的,但是出現(xiàn)了許多奇怪的慢SQL。
此時(shí)我大概猜測(cè)到了可能是什么操作鎖表了,導(dǎo)致大量Connection無(wú)法釋放,又趕緊看了Mysql鎖的情況,果然發(fā)現(xiàn)了大量的元數(shù)據(jù)鎖,高達(dá)400多個(gè)Connection沒(méi)釋放。
二、處理步驟
既然出現(xiàn)了元數(shù)據(jù)鎖,導(dǎo)致這么多Connection沒(méi)有釋放,那就找出占用時(shí)間最長(zhǎng)的那個(gè)會(huì)話kill掉。陸續(xù)kill了幾個(gè)會(huì)話后,系統(tǒng)恢復(fù)了。
系統(tǒng)恢復(fù)后,又去看了慢SQL,發(fā)現(xiàn)主要有兩塊高頻慢SQL,一塊是Flink相關(guān)的,另一塊是Nacos相關(guān)的。后來(lái)經(jīng)過(guò)分析:元數(shù)據(jù)鎖是因?yàn)镕link CDC執(zhí)行FLUSH TABLES WITH READ LOCK導(dǎo)致的,跟Nacos無(wú)關(guān),Nacos只是個(gè)煙霧彈。
# Flink相關(guān)的:
SHOW CREATE TABLE `xxx_db`.`xxx_table`;
FLUSH TABLES WITH READ LOCK;
# Nacos相關(guān)的:
DELETE FROM config_info WHERE data_id='com.alibaba.nacos.testMasterDB';
防止事故再次發(fā)生,又把Flink CDC任務(wù)里的SQL方式換成了API方式。Flink CDC使用SQL方式時(shí),會(huì)產(chǎn)生大量任務(wù),占用更多的資源,也容易出現(xiàn)任務(wù)異常。
三、原因分析
1.元數(shù)據(jù)鎖
(1) 以上關(guān)于鎖的截圖,可以看到是元數(shù)據(jù)鎖引發(fā)的Connection被耗盡,那什么是元數(shù)據(jù)鎖:
- 元數(shù)據(jù)鎖(Meta Data Lock,MDL),用于鎖定數(shù)據(jù)庫(kù)對(duì)象的元數(shù)據(jù),例如:表、索引、視圖等的結(jié)構(gòu)信息。通常用于保證并發(fā)的數(shù)據(jù)定義語(yǔ)言(DDL)操作的一致性,防止在修改表結(jié)構(gòu)的過(guò)程中出現(xiàn)并發(fā)問(wèn)題。
- 其作用是用于解決DDL操作與DML操作的一致性;通常,DDL操作需要獲取MDL寫(xiě)鎖,并且MDL鎖一旦發(fā)生,就可能會(huì)對(duì)數(shù)據(jù)庫(kù)的性能影響,因?yàn)楹罄m(xù)對(duì)該表的任何Select、DML、DDL操作都會(huì)被阻塞,造成Connection積壓。
(2) 為什么要有元數(shù)據(jù)鎖:
主要為了保證元數(shù)據(jù)的一致性,用于處理不同線程操作同一數(shù)據(jù)對(duì)象的同步與互斥問(wèn)題。比如需要事務(wù)隔離場(chǎng)景、主從同步場(chǎng)景。
(3) 元數(shù)據(jù)鎖和Innodb鎖的區(qū)別:
- 元數(shù)據(jù)鎖主要關(guān)注數(shù)據(jù)庫(kù)對(duì)象的元信息,而InnoDB鎖主要關(guān)注數(shù)據(jù)的一致性和隔離性。
- MDL鎖還能實(shí)現(xiàn)其他粒度級(jí)別的鎖,比如:全局鎖、庫(kù)級(jí)別的鎖、表空間級(jí)別的鎖。這是InnoDB存儲(chǔ)引擎不能直接實(shí)現(xiàn)的。
(4) 鎖表的原理是數(shù)據(jù)庫(kù)使用獨(dú)占式鎖機(jī)制。鎖表發(fā)生在 insert、update、delete中。比如:A程序執(zhí)行了對(duì)table_1的insert、update、delete,并還未commit時(shí),B程序也對(duì)table_1進(jìn)行insert、update、delete時(shí)會(huì)發(fā)生資鎖表。
2.Flink CDC為什么引起元數(shù)據(jù)鎖事故
筆者使用Flink場(chǎng)景是,利用Flink CDC同步數(shù)據(jù),然后做匯總統(tǒng)計(jì)。
(1) MySQL CDC如何工作
- 在 CDC 過(guò)程中,F(xiàn)link 需要定期讀取數(shù)據(jù)源的變化并進(jìn)行處理。需要元數(shù)據(jù)鎖 確保在讀取元數(shù)據(jù)(例如數(shù)據(jù)庫(kù)表的結(jié)構(gòu)信息)時(shí),沒(méi)有其他并發(fā)的操作修改了這些元數(shù)據(jù),從而保證 Flink 的元數(shù)據(jù)和實(shí)際數(shù)據(jù)的一致性。
- 啟動(dòng)MySQL CDC源時(shí),它將執(zhí)行FLUSH TABLES WITH READ LOCK,獲取一個(gè)全局讀取鎖,防止其他會(huì)話對(duì)這些表進(jìn)行寫(xiě)操作,從而保證捕獲的數(shù)據(jù)的一致性和準(zhǔn)確性。該鎖將阻止其他寫(xiě)入操作。
- 然后,它讀取當(dāng)前binlog位置以及數(shù)據(jù)庫(kù)和表的schema。
- 之后,將釋放全局讀取鎖。然后,它掃描數(shù)據(jù)庫(kù)表并從先前記錄的位置讀取binlog。
- 如果發(fā)生故障,任務(wù)將重新啟動(dòng)。
(2) 元數(shù)據(jù)鎖原因
因?yàn)镕link CDC啟動(dòng)時(shí)執(zhí)行FLUSH TABLES WITH READ LOCK直接上讀取鎖,由于時(shí)間較長(zhǎng),此時(shí)有大量的insert、update、delete操作一直處于等待,導(dǎo)致Mysql Connection無(wú)法釋放。
正好此時(shí),F(xiàn)link CDC執(zhí)行同步任務(wù)時(shí),又出現(xiàn)了異常,然后任務(wù)重啟,重啟后是上鎖,結(jié)果出現(xiàn)了惡性循環(huán)。導(dǎo)致更多的的insert、update、delete操作處于等待,導(dǎo)致更多的Myql Connection無(wú)法釋放,直接Connection全部耗盡。
然后所有應(yīng)用都拿不到Mysql Connection,所以系統(tǒng)徹底不可用了。
至于Nacos為什么會(huì)執(zhí)行DELETE FROM config_info WHERE data_id='com.alibaba.nacos.testMasterDB'呢?查閱資料后發(fā)現(xiàn),Nacos也是從Mysql獲取Connection的,當(dāng)Mysql出現(xiàn)問(wèn)題時(shí),比如死鎖、Connection耗盡、CPU打滿時(shí),都會(huì)執(zhí)行這個(gè)SQL。