企业电子商务网站建设规划方案,帝国+只做网站地图,wordpress表情包,湖南住房和城乡建设厅网站前言Instant add or drop column的主线逻辑表定义的列顺序与row 存储列顺序阐述引入row版本的必要性数据腐化问题原因分析Bug重现与解析MySQL8.0.30修复方案 前言
DDL 相对于数据库的 DML 之类的其他操作#xff0c;相对来说是比较耗时、相对重型的操作; 因此对业务的影比较严…前言Instant add or drop column的主线逻辑表定义的列顺序与row 存储列顺序阐述引入row版本的必要性数据腐化问题原因分析Bug重现与解析MySQL8.0.30修复方案 前言
DDL 相对于数据库的 DML 之类的其他操作相对来说是比较耗时、相对重型的操作; 因此对业务的影比较严重。MySQL 从5.6版本开始一直在持续改进其DDL性能引入了 online DDLinplace DDLinstant DDL 等实用性极强的功能 DDL 目前对业务的影响持续降低。
MySQL 8.0.29 引入了 instant add/drop column 功能支持在任意位置添加 column, drop column 也不需要表数据的任何形式的移动 只需要修改表的元数据就可以完成 add/drop column所以 instant add/drop column 的操作是轻型操作速度快资源需求量少。
ALTER table drop column a, ALGORITHMINSTANT;
8.0.29 引入了新的alter 算法 INSTANT。
但是这个新功能目前很不稳定导致的问题比较多; 而且通常都比较严重: 数据损坏或者数据库无法启动等。
本文是分析其中的一个问题: 对表进行 instant drop 后进行 update 之后数据库停机而后数据库无法启动。
为分析这个问题 我们会从 instant add/drop column 在 Innodb 的实现原理与细节方面来阐述这个数据腐化bug的具体原因。
Instant add or drop column的主线逻辑
因为这个功能的WorkLog无法从官方获取所以无法得到准确的设计出发点通过阅读相关代码得出要实现这个功能必须要处理以下关键点
因为要支持在任意位置添加/删除列同时不会更改表数据文件所以表的逻辑定义与row的实际存储形式需要映射关系不再是所见即所得的一一对应的关系。即为了实现这样功能 表中列的定义顺序与表中行数据(row)的存储顺序是不同的。同时对同一个table可以做多次instant DDL, 所以需要引入版本机制在表的数据文件中,不同row对应的表定义可能是不同的需要在row中记住表定义的version。
以上可以认为是该功能的设计原则与实现的主线逻辑。
表定义的列顺序与row 存储列顺序阐述
在引入这个功能之前 create table 时列定义的顺序与列在 InnoDB 中存储的顺序是一致的。这里我们不用考虑 InnoDB 添加系统隐藏列
Instant add/drop column 要实现的亮点功能是在表定义的任意位置添加或者减少 column同时做这样的操作的时候能够做到不需要重构表数据。
我们称 column 在表定义中出现的顺序为逻辑顺序;
而 column 在行数据的存储顺序为物理顺序。
要做到修改表定义而不重构表数据就必须将逻辑顺序与物理顺序解耦: 不能再像MySQL 8.0.29之前的版本那样逻辑顺序与物理顺序是完全一致的而从8.0.29开始通过表的元数据保存了逻辑顺序与物理顺序的映射关系。这种映射关系的构建与维护构成了 instant add/drop column 的基础.
如下图简单阐述了逻辑/物理顺序的关系。 引入row版本的必要性
对于同一张表Instant add/drop DDL可以执行多次每一次执行后逻辑/物理顺序的映射关系就发生变化同时 instant add/drop DDL 并不需要做表数据的重构操作因此可以得出经过多次 instant add/drop DDLInnoDB存储的行数据与表定义存在多种逻辑/物理顺序映射关系比如说在 ibd 文件中前十行数据对应原始的表定义接下来的十行可能对应着 instant add column 后的数据再接下来的十行可能对应着 instant drop column 后的数据。
为了管理这种形式的逻辑/物理在 InnoDB 中为每一行实际存储的数据引入了版本号的概念每个版本号对应着一个逻辑/物理映射关系。
为存储这个版本信息InnoDB 中row 的信息头记录的格式有稍微的变化: 如上图所示在row的extra中存储了其对应的版本号 同时在 row header 中有标志位指示出了是否存在版本号信息。
根据版本号获取相应的映射关系就可以正确的解析行数据。
目前版本号最大支持到64 instant add/drop column 到达这个限制后报错其后如果还需要 instant add/drop column DDL 操作可能需要做一次能够触发 table rebuild 操作才可以。
数据腐化问题
由 instant add/drop column 引入了多个数据腐化问题其中一个问题可以从
[PS-8292] MySQL 8.0.29 fails to perform crash recovery - Percona JIRA(https://jira.percona.com/browse/PS-8292) 查看。
这个问题简单来说在对表进行 instant drop 后进行update操作之后MySQL server 重启在启动阶段恢复之前的 update 操作会引发 assert 崩溃(debug版本的情况下)。
从代码上看这个bug可能会造成数据的静默错误(数据完全错乱而且不报任何错误)而不仅仅是崩溃这一种现象。
通过对core文件的简单分析造成该问题的大概原因如下
在通过redo做恢复的时候字段的逻辑顺序与物理存储顺序之间的映射关系不对(错位)导致的。在恢复期间可能会找不到对应的字段或者更新了错误的字段。
原因分析
从原始的问题看这个是发生在 InnoDB 启动恢复阶段。这一阶段离不开 redo log的参与。前面介绍 instant add/drop 设计要点的时候那些列出的要点可以认为是在在 DDL 期间的工作以及编码的基本逻辑那么在完成 instant DDL 时候 在 DML 的时候也需要将必要的信息写入 redo log 才能做到 recovery。
为支持 instant add/drop columnredo log 记录的格式发生了变化因为代码bug导致在解析 redo log 做恢复的时候得到的字段信息错误导致数据腐化。问题表现出来可能是: 恢复始终无法执行数据库无法启动还可能是恢复到错误的数据数据库能够启动。
因为 redo log 的种类较多信息也比较繁杂这里我们只关注问题本身中出现的 update 相关的 redo log 进而较多的关注 update redo log 与该问题相关的字段信息。
下图简要的阐述了 update redo log 相关内容: 到这里可以看到 在MySQL 8.0.29中update redo log 引入了 instant column 的物理逻辑顺序。
下面从 InnoDB 的恢复流程跟踪问题发生的原因其中主要需要关注的是恢复过程中的表(索引)定义。
应用 redo log 是在数据库启动阶段最开始就执行此时数据字典无法打开获取不到待恢复表的定义信息但是此时需要表的定义信息去解析 redo log 中的相关数据此时就会根据redo log中记录的长度信息以及记录长度的顺序构建临时的表定义此时仅仅是为了恢复并不需要精确的表定义此时只需要知道field的长度和位置即可。同时如果 redo log 中如果有instant DDL 的信息那么也会用这些信息去修改临时构建的表定义:这是问题发生的初始错误的地方。恢复过程中构建出的临时表实际上表中列的逻辑顺序这是符合正常运行的需求的。但是实际上8.0.29中字段长度的记录顺序是按字段(列)的物理存储顺序写入的。如果带有 instant DDL 的信息那么修改表定义时就会按物理顺序去修改逻辑顺序的表定义这样会修改到非预期的字段导致错误发生!
Bug重现与解析
CREATE TABLE tb1 (col1 VARCHAR(10) NOT NULL,col2 char(13),col3 varchar(11),PRIMARY KEY (col1)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;
INSERT INTO tb1 VALUES (4000,50,100);
--echo # the FIRST INSTANT ALTER
ALTER TABLE tb1 DROP COLUMN col2, LOCKDEFAULT;
INSERT INTO tb1 VALUES( 4545, 52 );
UPDATE tb1 SET col3 46 WHERE col1 4545;
--echo # crash and restart 1
--source include/kill_and_restart_mysqld.inc
CHECK TABLE tb1;
DROP TABLE tb1;
以上MySQL MTR 测例可以重现 InnoDB 启动恢复期间始终 core 的问题。我们从这个例子出发结合上面解释的 instant drop DDL 代码行为看看问题是如何一步步发生的。
首先说明一下在测例运行期间逻辑顺序与物理顺序的变化。 如下图所示稍微展示了 table 的逻辑定于与 InnoDB row 存储的以下细节。这里注意的是 被 dropped column 仍然会以隐藏列的形式存在于表定于中因为 drop 之前存在的 row 还是需要这样信息解析字段。 结合 redo log 的恢复过程看看问题发生的第一现场。这里针对这个测例摘取相关 redo log 的部分信息 2.1 按照字段长度列表8.0.29中是物理顺序写入的列表创建的专门用于恢复的表类似于: create table dummy_table (d1:10, d2:13, d3:11) 2.2 按照 instant 字段信息修改 dummy 表按照 physical pos1 去修改后结果类似于create table dummy_table (d1:10, d2:13[dropped], d3:11) 2.3 期望的正确的表应该类似于create table dummy_table(d1:10, d3:11, d2:13[dropped]); 2.4 Redo log中的Field_no1, 去恢复时期望用到的是 #2.3 的表但是过程中创建的是#2.2中错误的表这样当Field_no1去恢复数据时会错误的发现对应的field(column)已经dropped, 导致core
MySQL8.0.30修复方案
知道了问题发生的原因,修复起来就比较简单了
MySQL 8.0.30的代码修复方案 Redo log中字段的长度列表按照字段的逻辑顺序写入不再按存储顺序写入。在 redo log 的 instant column 信息中也包含了字段的逻辑位置。Redo log 的记录本身的版本设置为 1 与8.0.29的版本为 0 做出差别。8.0.30的修复代码本身也是不能正确解析8.0.29产生的 redo log 只是根据版本号检测出8.0.29 redo log进而报错防止数据进一步恶化。实际上8.0.29的 redo log 在 instant DDL 后是不可能正确解析的因为没有逻辑/物理的映射关系。 修复的逻辑比较简单: Redo log中字段的长度列表按照字段的逻辑顺序写入 保证在恢复阶段构建的临时表是按正确的逻辑定义顺序构建的。 在redo log 的 instant column 信息中也包含字段的逻辑位置: 保证在更新临时表的字段时按照逻辑顺序不会出现错误更新的情况。
下面是MySQL 8.0.30 update redo log 相关字段信息: 从上图可以看出MySQL 8.0.30 redo log 中已经不存储物理位置相关的信息了全部是逻辑位置相关的信息这样就和MySQL 8.0.29 redo log 这种有问题的记录方式是昙花一现了。
附带的这个测例可以重现数据的静默错误(恢复过程没问题 但是数据实际上错了)
CREATE TABLE tb2 ( c1 char(4) NOT NULL, c2 char(4), c3 char(4), PRIMARY KEY (c1)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;
begin;
INSERT INTO tb2 VALUES (1000,2000,3000);
commit;
--echo # the FIRST INSTANT ALTER
ALTER TABLE tb2 add COLUMN c4 char(4) after c1, LOCKDEFAULT;
INSERT INTO tb2 VALUES (1001,4001, 2001, 3001);
SELECT * FROM tb2;
UPDATE tb2 set c44002 WHERE c11001;
--echo # crash and restart 1
--source include/kill_and_restart_mysqld.inc
select * from tb2;
CHECK TABLE tb2;
需要把这个测例放到innodb test case suite中。 Enjoy GreatSQL :)
关于 GreatSQL
GreatSQL是由万里数据库维护的MySQL分支专注于提升MGR可靠性及性能支持InnoDB并行查询特性是适用于金融级应用的MySQL分支版本。
相关链接 GreatSQL社区 Gitee GitHub Bilibili
GreatSQL社区 社区有奖建议反馈 https://greatsql.cn/thread-54-1-1.html
社区博客有奖征稿详情 https://greatsql.cn/thread-100-1-1.html
社区2022年度勋章获奖名单 https://greatsql.cn/thread-184-1-1.html
对文章有疑问或者有独到见解都可以去社区官网提出或分享哦~
技术交流