加入收藏 | 设为首页 | 会员中心 | 我要投稿 银川站长网 (https://www.0951zz.com/)- 云通信、基础存储、云上网络、机器学习、视觉智能!
当前位置: 首页 > 站长学院 > MySql教程 > 正文

如何理解MySQL中的table_id

发布时间:2023-07-19 13:45:24 所属栏目:MySql教程 来源:
导读:本篇内容介绍了“怎么理解MySQL中的table_id”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一

本篇内容介绍了“怎么理解MySQL中的table_id”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一 table_id 介绍

   当MySQL 开启日志模式时,binlog会记录所有对数据库的变更操作。binlog 分两种模式 statement 模式和row 模式。

当数据库的binlog format 是statement 模式时

例子:数据库中执行 一条语句

root@rac2 [yangyi]> insert into t1 values(9);        

Query OK, 1 row affected (0.00 sec)

root@rac2 [yangyi]> show binlog events in 'mysql-bin.000003';

+------------------+-----+-------------+-----------+-------------+----------------------------------------+

| Log_name    | Pos | Event_type | Server_id | End_log_pos | Info                 |

+------------------+-----+-------------+-----------+-------------+----------------------------------------+

| mysql-bin.000003 | 4 | Format_desc |    2 |    106 | Server ver: 5.1.68-log, Binlog ver: 4 |

| mysql-bin.000003 | 106 | Query   |    2 |    176 | BEGIN                 |

| mysql-bin.000003 | 176 | Query   |    2 |    265 | use `yangyi`; insert into t1 values(8) |

| mysql-bin.000003 | 265 | Xid    |    2 |    292 | COMMIT /* xid=12 */          |

| mysql-bin.000003 | 292 | Query   |    2 |    369 | use `yangyi`; flush tables      |

| mysql-bin.000003 | 369 | Query   |    2 |    439 | BEGIN                 |

| mysql-bin.000003 | 439 | Query   |    2 |    528 | use `yangyi`; insert into t1 values(9) |

| mysql-bin.000003 | 528 | Xid    |    2 |    555 | COMMIT /* xid=15 */          |

+------------------+-----+-------------+-----------+-------------+----------------------------------------+

8 rows in set (0.00 sec)

binlog 的log event 记录如下:

#140511 14:44:12 server id 2 end_log_pos 439 Query thread_id=1  exec_time=0  error_code=0

SET TIMESTAMP=1399790652/*!*/;

BEGIN

/*!*/;

# at 439

#140511 14:44:12 server id 2 end_log_pos 528 Query thread_id=1  exec_time=0  error_code=0

SET TIMESTAMP=1399790652/*!*/;

insert into t1 values(9)

/*!*/;

# at 528

#140511 14:44:12 server id 2 end_log_pos 555 Xid = 15

COMMIT/*!*/;

从日志分析来看 ,DML会记录为原始的SQL,也就是记录在QUERY_EVENT中。 

当数据库的binlog format 是row模式时

执行insert 操作

root@rac2 [yangyi]> insert into t1 values(6);        

Query OK, 1 row affected (0.00 sec)

root@rac2 [yangyi]> show binlog events in 'mysql-bin.000002';     

+------------------+-----+-------------+-----------+-------------+---------------------------------------+

| Log_name    | Pos | Event_type | Server_id | End_log_pos | Info                 |

+------------------+-----+-------------+-----------+-------------+---------------------------------------+

| mysql-bin.000002 | 4 | Format_desc |    2 |    106 | Server ver: 5.1.68-log, Binlog ver: 4 |

| mysql-bin.000002 | 106 | Query   |    2 |    176 | BEGIN                |

| mysql-bin.000002 | 176 | Table_map |    2 |    219 | table_id: 18 (yangyi.t1)       |

| mysql-bin.000002 | 219 | Write_rows |    2 |    253 | table_id: 18 flags: STMT_END_F    |

| mysql-bin.000002 | 253 | Xid    |    2 |    280 | COMMIT /* xid=61 */         |

+------------------+-----+-------------+-----------+-------------+---------------------------------------+

5 rows in set (0.00 sec)

binlog中记录的信息:

BEGIN

/*!*/;

# at 176

# at 219

#140511 14:31:43 server id 2 end_log_pos 219 Table_map: `yangyi`.`t1` mapped to number 18

#140511 14:31:43 server id 2 end_log_pos 253 Write_rows: table id 18 flags: STMT_END_F

BINLOG '

TxlvUxMCAAAAKwAAANsAAAAAABIAAAAAAAEABnlhbmd5aQACdDEAAQMAAQ==

TxlvUxcCAAAAIgAAAP0AAAAAABIAAAAAAAEAAf/+BgAAAA==

'/*!*/;

### INSERT INTO `yangyi`.`t1`

### SET

### @1=6 /* INT meta=0 nullable=1 is_null=0 */

# at 253

#140511 14:31:43 server id 2 end_log_pos 280 Xid = 61

COMMIT/*!*/;

  从解析的binlog中可以看出row模式下,DML操作会记录为:TABLE_MAP_EVENT+ROW_LOG_EVENT(包括WRITE_ROWS_EVENT ,UPDATE_ROWS_EVENT,DELETE_ROWS_EVENT).

  为什么一个update在ROW模式下需要分解成两个event:一个Table_map,一个Update_rows。我们想象一下,一个update如果更新了10000条数据,那么对应的表结构信息是否需要记录10000次?其实是对同一个表的操作,所以这里binlog只是记录了一个Table_map用于记录表结构相关信息,而后面的Update_rows记录了更新数据的行信息。他们之间是通过table_id来联系的。 

二 table_id 的特性

 1 table_id 并不是固定的,它是当表被载入内存(table_definition_cache)时,临时分配的,是一个不断增长的变量。 

 2 当有新的table变更时,在cache中没有,就会触发一次load table def的操作,此时就会在原先最后一次table_id基础上+1,做为新的table def的id。

 3 flush tables,之后对表的更新操作也会触发table_id 的增长。

 4 如果table def cache过小,就会出现频繁的换入换出,从而导致table_id增长比较快。

例子

root@rac2 [yangyi]> show binlog events in 'mysql-bin.000002';

+------------------+-----+-------------+-----------+-------------+---------------------------------------+

| Log_name    | Pos | Event_type | Server_id | End_log_pos | Info                 |

+------------------+-----+-------------+-----------+-------------+---------------------------------------+

| mysql-bin.000002 | 4 | Format_desc |    2 |    106 | Server ver: 5.1.68-log, Binlog ver: 4 |

| mysql-bin.000002 | 106 | Query   |    2 |    176 | BEGIN                |

| mysql-bin.000002 | 176 | Table_map |    2 |    219 | table_id: 18 (yangyi.t1)       |

| mysql-bin.000002 | 219 | Write_rows |    2 |    253 | table_id: 18 flags: STMT_END_F    |

| mysql-bin.000002 | 253 | Xid    |    2 |    280 | COMMIT /* xid=61 */         |

| mysql-bin.000002 | 280 | Query   |    2 |    357 | use `yangyi`; flush tables       |

| mysql-bin.000002 | 357 | Query   |    2 |    427 | BEGIN                |

| mysql-bin.000002 | 427 | Table_map |    2 |    470 | table_id: 19 (yangyi.t1)       |

| mysql-bin.000002 | 470 | Write_rows |    2 |    504 | table_id: 19 flags: STMT_END_F    |

| mysql-bin.000002 | 504 | Xid    |    2 |    531 | COMMIT /* xid=65 */         |

+------------------+-----+-------------+-----------+-------------+---------------------------------------+

10 rows in set (0.00 sec)

三 table_id在主从复制过程中转变 

   每一个dml操作表的信息都被会记录table_mapping的hash数据结构中,hash的key就是ulong型的table_id,hash的值就是TABLE*的数据结构(包含了表的各种信息,包括数据库名,表名,字段数,字段类型等),通过set_table()方法来hash,通过get_table()方法来根据table_id获得对应的表信息。

   当主库的日志传递到备库时,每一个log_event都是通过do_apply_event()方法来将event应用到本地数据库中。在apply relay log中的event时,do_apply_event()将ulong型的m_table_id(binlog记录的table_id)赋值给RPL_TABLE_LIST结构中的uint型的table_id。核心问题出现了: 如果binlog 中的table_id 的值大于max(uint),在变量传递是,就会发生截断。

而MySQL内部使用set_table(table_id)构造hash,使用get_table(m_table_id)从hash表中取值,在两个阶段用到的key因为发生了数据截断,所以必然也就不能取到预期的值。也就是说之前用uint型的table_id构建出来的key-value的hash对,用ulong型的m_table_id是无法查询到的。

四 风险与解决

 从第二,三点我们知道当table_id 过快增长,会导致从库应用binlog无法解析到对应的表,造成数据不一致的情况。

解决方法:

 1 加大 table cache 的大小。

 2 重启主库使table_id 归0,缺点 成本比较高,出现此问题的时候,主备已经不一致,线上环境 不能完成切换。

 3 修改MySQL源码,将 RPL_TABLE_LIST结构中的uint型的table_id修改为ulong型 ,一劳永逸。

(编辑:银川站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章