Binlog中table_map_id 的探究


背景:

最近,線上Row Based Replication(下稱RBR)環境中遇到了一個Bug。這個bug簡單的描述就是:RBR對於DML需要通過table-map的event來標注每一個有更新的表。

而當一個DML同時操作多個表,且其中2個表的mapid相同時(通常為0),會導致slave執行這個event時crash,並重啟mysqld實例

可見這個bug的毀滅性極大。

那么table-map-id 究竟從何而來?有什么辦法知道每個表table-map-id,從而進行一些必要的監控呢?

下文將用幾個例子來進行分析說明。

 

1. table-map-id 和 Innodb的table-id是否是同一個概念?

其實這個問題的答案是顯而易見的。因為並非Innodb的表才支持RBR,如果這個問題答案為“是”,那么非innodb的表在RBR中的table-map-id從何而來呢?又怎么保證和innodb的map-id不重復呢?

所以,顯然table-map-id和Innodb數據字典中的table-id是完全不同的兩個概念。

即便如此,下面還是用一個實例進行驗證

create table map_id_test (ID int primary key);
insert into map_id_test values (1);
show binlog events in 'log-prefix.000025';

輸出結果:

Log_name             Pos     Event_type   Server_id    End_log_pos    Info
log-prefix.000025    2156    Query        15757        2224           BEGIN
log-prefix.000025    2224    Table_map    15757        2274           table_id: 88 (test.map_id_test)
log-prefix.000025    2274    Write_rows   15757        2308           table_id: 88 flags: STMT_END_F
log-prefix.000025    2308    Xid          15757        2335           COMMIT /* xid=346 */

查看Innodb的table-id:

select TABLE_ID from INNODB_SYS_TABLESTATS where `SCHEMA`='test' and NAME='map_id_test';

得到TABLE_ID = 170

 

 

2. table-map-id是否和物理文件有綁定關系

雖然table-map-id和Innodb的table-id是完全不同的概念。而我們知道Innodb中的table-id和物理文件有綁定關系,即rename table的操作不會改變dict-table中的table-id。

那么binlog中的table-map-id是不是有可能借鑒了這種實現方式,也有這個特性呢?

下面是具體測試過程

set global binlog_format='row';
create table map_id_test1 (ID int primary key);
create table map_id_test2 (ID int primary key);
insert into map_id_test1 values (1);
insert into map_id_test2 values (1);
show binlog events in 'log-prefix.000025';

輸出結果如下:此時table1 對應table_id:83 , tabl2 對應table_id:84 

Log_name             Pos     Event_type   Server_id End_log_pos    Info
log-prefix.000025    1157    Query        15757     1225           BEGIN
log-prefix.000025    1225    Table_map    15757     1276           table_id: 83 (test.map_id_test1)
log-prefix.000025    1276    Write_rows   15757     1310           table_id: 83 flags: STMT_END_F
log-prefix.000025    1310    Xid          15757     1337           COMMIT /* xid=327 */
log-prefix.000025    1337    Query        15757     1405           BEGIN
log-prefix.000025    1405    Table_map    15757     1456           table_id: 84 (test.map_id_test2)
log-prefix.000025    1456    Write_rows   15757     1490           table_id: 84 flags: STMT_END_F
log-prefix.000025    1490    Xid          15757     1517           COMMIT /* xid=330 */

執行rename table,交換table1和table2

rename table map_id_test1 to map_id_test1_bak,map_id_test2 to map_id_test1, map_id_test1_bak to map_id_test2;

 查看binlog:此時table1 對應table_id:86 , tabl2 對應table_id:87。

Log_name             Pos     Event_type    Server_id    End_log_pos    Info
log-prefix.000025    1688    Query         15757        1756           BEGIN
log-prefix.000025    1756    Table_map     15757        1807           table_id: 86 (test.map_id_test1)
log-prefix.000025    1807    Write_rows    15757        1841           table_id: 86 flags: STMT_END_F
log-prefix.000025    1841    Xid           15757        1868           COMMIT /* xid=334 */
log-prefix.000025    1868    Query         15757        1936           BEGIN
log-prefix.000025    1936    Table_map     15757        1987           table_id: 87 (test.map_id_test2)
log-prefix.000025    1987    Write_rows    15757        2021           table_id: 87 flags: STMT_END_F
log-prefix.000025    2021    Xid           15757        2048           COMMIT /* xid=335 */

從實驗可以得出結論,RBR中的table_id 不僅和物理文件沒有綁定關系,在MySQL實例的運行過程中也不是靜態不變的。

因此,大膽猜測,table_id 和file handler有關系。下面的測試將進行驗證。

 

3. table_id 和file handler是否有直接聯系?

insert into map_id_test1 values (3);
flush tables;
insert into map_id_test1 values (4);
show binlog events in 'log-prefix.000025';

執行結果: 從結果可以看出,flush table導致了,file handler的重新打開。同時也使table-map-id 發生了變化,且線性遞增。

Log_name             Pos     Event_type    Server_id    End_log_pos    Info
log-prefix.000025    2424    Query         15757        2492           BEGIN
log-prefix.000025    2492    Table_map     15757        2543           table_id: 89 (test.map_id_test1)
log-prefix.000025    2543    Write_rows    15757        2577           table_id: 89 flags: STMT_END_F
log-prefix.000025    2577    Xid           15757        2604           COMMIT /* xid=383 */
log-prefix.000025    2604    Query         15757        2679           use `test`; flush tables
log-prefix.000025    2679    Query         15757        2747           BEGIN
log-prefix.000025    2747    Table_map     15757        2798           table_id: 90 (test.map_id_test1)
log-prefix.000025    2798    Write_rows    15757        2832           table_id: 90 flags: STMT_END_F
log-prefix.000025    2832    Xid           15757        2859           COMMIT /* xid=385 */

 

4 代碼分析

出現table_map_id = 0 的主要原因在於下列代碼標紅部分 (sql_base.cc: line 3079)

alter table,且不涉及數據更新時,新表的table_map_id 使用了原表已經關閉的table_map_id (此時值為 ~0UL) 。

mysql表示在5.1.53后修復了這個bug

  /* This list copies variables set by open_table */
  tmp.tablenr=          table->tablenr;
  tmp.used_fields=      table->used_fields;
  tmp.const_table=      table->const_table;
  tmp.null_row=         table->null_row;
  tmp.maybe_null=       table->maybe_null;
  tmp.status=           table->status;

  tmp.s->table_map_id= table->s->table_map_id; /* Get state */
  tmp.in_use=           thd;
  tmp.reginfo.lock_type=table->reginfo.lock_type;
  tmp.grant=            table->grant;

 

 

結論:

1. RBR中的Table_ID 和Innodb中的table_id 沒有關系,且和物理文件沒有對應關系。

2. Flush Table 可以重置RBR中的Table_ID ,如果有表遇到了map_id=0 的情況,可以使用這個方法嘗試解決問題。

3. 雖然和File Handler 有關,但是和 /proc/$PID/fd/ 中的fd數值沒有直接聯系

 

 

 

 

 

 

 

 


免責聲明!

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



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