MySQL table_id原理及風險分析


1. 什么是table_id

MySQL binlog文件按格式分為文件頭部和事件信息。文件頭部占4字節,內容固定為:"\xfe\x62\x69\x6e",接下來就是各個event了。event有多種類型,比如ROTATE_EVENT對應的記錄了binlog切換到下一個binlog文件的信息,XID_EVENT記錄了一個事務提交的相關信息。

binlog_format可以設置為statement和row的方式。當設置為statement情況下,DML會記錄為原始的SQL,也就是記錄在QUERY_EVENT中。而row會記錄為TABLE_MAP_EVENT+ROW_LOG_EVENT(包括WRITE_ROWS_EVENT,UPDATE_ROWS_EVENT,DELETE_ROWS_EVENT)。

binlog_format設置為row時,執行一句insert,對應的binlog如下所示:

為什么一個insert在row模式下需要分解成兩個event:一個Table_map,一個Write_rows?假如一個insert更新了10000條數據,那么對應的表結構信息是否需要記錄10000次列?其實是對同一個表的操作,所以這里binlog只是記錄了一個Table_map用於記錄表結構相關信息,而后面的Write_rows記錄了更新數據的行信息。他們之間是通過table_id來聯系的。

table_id用來做hash key,通過set_table(table_id)的方法將某個表的信息hash到cache中;又可以通過get_table()方法來根據table_id獲得對應的表信息。

要注意table_id並不是固定的綁定在一個表上,它是表載入table cache時臨時分配的,一個不斷增長的變量。

 

2. table_id的增長機制

連續往同一個table中進行多次DML操作,table_id不變。 一般來說,出現DDL操作時,table_id才會變化。

 

下圖中有3個表(t1、t2、t3),執行flush tables,再進行DML操作,每個表的table_id都在增長。

如果表太多,又有頻繁的flush tables,會導致table_id增長比較快。

 

根據MySQL binlog table_id源碼分析 ,可以知道:

table id的變化依賴於table cache中是否存儲了binlog操作表的表定義。如果table cache中存在,則table id不變;而當table cache中不存在時,該值根據上一次操作的table id自增1。因此,table id與實際操作的數據表沒有直接對應關系,而與操作的數據表是否在table cache中有關。此外,table_definition_cache中默認存放400個表定義,如果超出該范圍,會將最久未用的表定義置換出table cache

 

3. table_id快速增長的風險

binlog中table_id是一個ulong類型(無符號長整形),在slave進行重做binlog events之前,會先將這個ulong的table_id(為了避免混淆,用m_table_id表示)傳給一個它內部維護的一個數據結構RPL_TABLE_LIST,這個里面有一個變量table_id用來存儲binlog中的m_table_id,問題出現了:數據結構的變量table_id是一個uint(無符號整形),如果m_table_id超過uint的范圍會發生截斷。而MySQL內部在構造hash,從hash表中取值是這樣的做法:set_table(table_id),get_table(m_table_id),在兩個階段用到的key因為發生了數據截斷所以必然也就不能取到預期的值。也就是說之前用uint型的table_id構建出來的key-value的hash對,用ulong型的m_table_id是無法查詢到的。

具體的源碼分析可以參考:淘寶物流MySQL slave數據丟失詳細原因

 
4. 如何避免table_id的風險
第一,增大table cache
第二,定期檢查 table_id,其值不能超過uint的范圍(重啟主庫)
第三,將RPL_TABLE_LIST這個內部數據結構里面的table_id類型改為ulong(修改MySQL源碼)
 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM