【MySQL】再说MySQL中的 table_id

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介:
【背景】
最近线上一个实例出现了主从数据不一致的情况,也即从库丢失数据的情况。根本原因:"由于table_list->table_id为uint,而m_table_id为ulong,主库上assign的table map id 总是一直递增的
当超过2^32后,备库出现溢出,导致row模式下备库对应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型 ,一劳永逸。
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
SQL 关系型数据库 MySQL
【MySQL BUG】线上系统因table_id溢出导致主从复制无法进行总结
上周,所负责一个系统遇到MySQL的bug(Bug #67352),导致所有从库卡在某一个点无法继续同步主库的更新。具体表现:主库后面挂载的10几个从库都停在同一个点,Relay_Master_Log_File和Exec_Master_Log_Pos一直不停滚动,但SQL却没有被执行,即relay log有被扫描的行为,但却没有被真正执行的行为。
2763 0
|
2月前
|
存储 SQL 关系型数据库
Mysql学习笔记(二):数据库命令行代码总结
这篇文章是关于MySQL数据库命令行操作的总结,包括登录、退出、查看时间与版本、数据库和数据表的基本操作(如创建、删除、查看)、数据的增删改查等。它还涉及了如何通过SQL语句进行条件查询、模糊查询、范围查询和限制查询,以及如何进行表结构的修改。这些内容对于初学者来说非常实用,是学习MySQL数据库管理的基础。
132 6
|
15天前
|
SQL 关系型数据库 MySQL
12 PHP配置数据库MySQL
路老师分享了PHP操作MySQL数据库的方法,包括安装并连接MySQL服务器、选择数据库、执行SQL语句(如插入、更新、删除和查询),以及将结果集返回到数组。通过具体示例代码,详细介绍了每一步的操作流程,帮助读者快速入门PHP与MySQL的交互。
29 1
|
17天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
31 4
|
2月前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
64 3
Mysql(4)—数据库索引
|
2月前
|
SQL Ubuntu 关系型数据库
Mysql学习笔记(一):数据库详细介绍以及Navicat简单使用
本文为MySQL学习笔记,介绍了数据库的基本概念,包括行、列、主键等,并解释了C/S和B/S架构以及SQL语言的分类。接着,指导如何在Windows和Ubuntu系统上安装MySQL,并提供了启动、停止和重启服务的命令。文章还涵盖了Navicat的使用,包括安装、登录和新建表格等步骤。最后,介绍了MySQL中的数据类型和字段约束,如主键、外键、非空和唯一等。
72 3
Mysql学习笔记(一):数据库详细介绍以及Navicat简单使用
|
24天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
125 1
|
26天前
|
关系型数据库 MySQL Linux
在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。
本文介绍了在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。同时,文章还对比了编译源码安装与使用 RPM 包安装的优缺点,帮助读者根据需求选择最合适的方法。通过具体案例,展示了编译源码安装的灵活性和定制性。
79 2
|
29天前
|
存储 关系型数据库 MySQL
MySQL vs. PostgreSQL:选择适合你的开源数据库
在众多开源数据库中,MySQL和PostgreSQL无疑是最受欢迎的两个。它们都有着强大的功能、广泛的社区支持和丰富的生态系统。然而,它们在设计理念、性能特点、功能特性等方面存在着显著的差异。本文将从这三个方面对MySQL和PostgreSQL进行比较,以帮助您选择更适合您需求的开源数据库。
106 4
|
12天前
|
运维 关系型数据库 MySQL
安装MySQL8数据库
本文介绍了MySQL的不同版本及其特点,并详细描述了如何通过Yum源安装MySQL 8.4社区版,包括配置Yum源、安装MySQL、启动服务、设置开机自启动、修改root用户密码以及设置远程登录等步骤。最后还提供了测试连接的方法。适用于初学者和运维人员。
112 0

热门文章

最新文章

下一篇
无影云桌面