MySQL table_id原理及風險分析

1. 什麼是table_idhtml

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

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)。sql

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得到對應的表信息。ui

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

 

2. table_id的增加機制.net

連續往同一個table中進行屢次DML操做,table_id不變。 通常來講,出現DDL操做時,table_id纔會變化。unix

 

下圖中有3個表(t一、t二、t3),執行flush tables,再進行DML操做,每一個表的table_id都在增加。orm

若是表太多,又有頻繁的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源碼)
相關文章
相關標籤/搜索