当前位置: 首页 > news >正文

MySQL 8.0.29 instant DDL 数据腐化问题分析

  • 前言
  • Instant add or drop column的主线逻辑
  • 表定义的列顺序与row 存储列顺序阐述
  • 引入row版本的必要性
  • 数据腐化问题
  • 原因分析
  • Bug重现与解析
  • MySQL8.0.30修复方案

前言

DDL 相对于数据库的 DML 之类的其他操作,相对来说是比较耗时、相对重型的操作; 因此对业务的影比较严重。MySQL 从5.6版本开始一直在持续改进其DDL性能:引入了 online DDL,inplace DDL,instant 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, ALGORITHM=INSTANT;

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 的基础.

如下图简单阐述了逻辑/物理顺序的关系。

image-20230612135907422

引入row版本的必要性

对于同一张表,Instant add/drop DDL可以执行多次;每一次执行后,逻辑/物理顺序的映射关系就发生变化;同时 instant add/drop DDL 并不需要做表数据的重构操作;因此可以得出经过多次 instant add/drop DDL,InnoDB存储的行数据与表定义存在多种逻辑/物理顺序映射关系:比如说,在 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 column,redo 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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO tb1  VALUES ('4000','50','100');
--echo # the FIRST INSTANT ALTER
ALTER TABLE tb1 DROP COLUMN col2, LOCK=DEFAULT;
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 代码行为看看问题是如何一步步发生的。

  1. 首先说明一下,在测例运行期间逻辑顺序与物理顺序的变化。 如下图所示稍微展示了 table 的逻辑定于与 InnoDB row 存储的以下细节。这里注意的是 被 dropped column 仍然会以隐藏列的形式存在于表定于中:因为 drop 之前存在的 row 还是需要这样信息解析字段。

图片

  1. 结合 redo log 的恢复过程看看问题发生的第一现场。这里针对这个测例摘取相关 redo log 的部分信息:

    图片

    2.1 按照字段长度列表(8.0.29中是物理顺序写入的列表)创建的专门用于恢复的表,类似于: create table dummy_table (d1:10, d2:13, d3:11)

    2.2 按照 instant 字段信息修改 dummy 表:按照 physical pos=1 去修改后,结果类似于: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_no=1, 去恢复时期望用到的是 #2.3 的表,但是过程中创建的是#2.2中错误的表,这样当Field_no=1去恢复数据时,会错误的发现对应的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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_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, LOCK=DEFAULT;
INSERT INTO tb2 VALUES ('1001','4001', '2001', '3001');
SELECT * FROM tb2;
UPDATE tb2 set c4='4002' WHERE c1='1001';
--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社区:

image

社区有奖建议反馈: 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

(对文章有疑问或者有独到见解都可以去社区官网提出或分享哦~)

技术交流:

image-20221030163217640

相关文章:

MySQL 8.0.29 instant DDL 数据腐化问题分析

前言Instant add or drop column的主线逻辑表定义的列顺序与row 存储列顺序阐述引入row版本的必要性数据腐化问题原因分析Bug重现与解析MySQL8.0.30修复方案 前言 DDL 相对于数据库的 DML 之类的其他操作,相对来说是比较耗时、相对重型的操作; 因此对业务的影比较严…...

Haproxy搭建负载均衡

Haproxy搭建负载均衡 一、常见的Web集群调度器二、Haproxy介绍1、Haproxy应用分析2、Haproxy的主要特性3、Haproxy负载均衡策略 三、LVS、Nginx、Haproxy之间的区别四、Haproxy搭建Web群集1、Haproxy服务器部署2、节点服务器部署3、测试Web群集 五、日志定义1、方法一2、方法二…...

SpringBoot:SpringBoot启动加载过程 ④

一、思想 我们看到技术上高效简单的使用,其实背后除了奇思妙想的开创性设计,另一点是别人帮你做了复杂繁琐的事情。 二、从官网Demo入手 官网就一行代码。这个就是它的启动代码。 1、SpringBootApplication注解 ①. 三个核心注解的整合。 SpringBootCon…...

抽象轻松JavaScript

真真假假,鬼鬼,谁知道什么是真什么是假 疑点二:什么是真,什么是假 核心:确定一个清晰的目的,可以达到目的就是真,达不到就是假 知道了核心开始举例 考大学,考上大学就是满足目的…...

深入理解CSS字符转义行为

深入理解CSS字符转义行为 深入理解CSS字符转义行为 前言为什么要转义&#xff1f;CSS 转义什么是合法css的表达式 左半部分右半部分 练习参考链接 前言 在日常的开发中&#xff0c;我们经常写css。比如常见的按钮: <button class"btn"></button>&am…...

【论文阅读】(2023.05.10-2023.06.03)论文阅读简单记录和汇总

(2023.05.10-2023.06.08)论文阅读简单记录和汇总 2023/05/10&#xff1a;今天状态&#xff0c;复阳大残&#xff0c;下午淋了点雨吹了点风&#xff0c;直接躺了四个小时还是头晕- -应该是阳了没跑了。 2023/06/03&#xff1a;前两周出差复阳&#xff0c;这两周调整作息把自己又…...

FPGA开发-ddr测试

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 提示&#xff1a;这里可以添加技术概要 例如&#xff1a; 本文以米联科开发板为例&#xff0c;介绍ddr测试相关例程。 整体架构流程 提示&#xff1a;这里可以添加技术整体架构 技术名词解释 提示&#xff1a;这…...

BUUCTF 大帝的密码武器 1

题目描述&#xff1a;&#xff08;下载题目&#xff0c;然后修改后缀名为.zip打开&#xff1a;&#xff09; 公元前一百年&#xff0c;在罗马出生了一位对世界影响巨大的人物&#xff0c;他生前是罗马三巨头之一。他率先使用了一种简单的加密函&#xff0c;因此这种加密方法以…...

Linux 查看目录个数和大小

一、查看某目录下的文件个数 1. 查看目录下的文件个数 即当前目录/hdapp目录下有多少个文件&#xff0c;文件夹不计入数值 ls -l | grep "^-"| wc -l下面的命令文件夹也会计入数值 ls | wc -w2. 查看指定目录下的文件个数 如果省略指定目录就是默认当前目录 ls…...

Unity制作二次元卡通渲染角色材质——4 、内外描边和细节添加

Unity制作二次元材质角色 回到目录 大家好&#xff0c;我是阿赵。 这里继续讲二次元角色材质。这次打算讲一下描边和细节的添加。 一、外描边 外描边的做法也不止一种&#xff0c;比如后处理方法的偏导数ddx/ddy之类的&#xff0c;也能整个屏幕的求出边缘。但一般来说单模型渲…...

Ubuntu安装GCC10

使用包安装的方式安装 sudo apt upgradesudo apt install software-properties-commonsudo add-apt-repository ppa:ubuntu-toolchain-r/test QA: 更新python3.7后出现ModuleNotFoundError: No module named ‘apt_pkg‘错误QA: Cannot import name ‘_gi’ sudo apt updatesu…...

【flutter】Dart 规范2

高效 Dart 语言指南&#xff1a;用法示例 每天在你写的 Dart 代码中都会应用到这些准则。库的使用者可能不需要知道你在其中的一些想法&#xff0c;但是维护者肯定是需要的。 库 这些准则可以帮助你在多个文件编写程序的情况下保证一致性和可维护性。为了让准则简洁&#xf…...

k8s CoreDns详解

一、概述 服务发现是 K8s 的一项很重要的功能。K8s 的服务发现有两种方式&#xff0c;一种是将 svc 的 ClusterIP 以环境变量的方式注入到 pod 中&#xff1b;一种就是 DNS&#xff0c;从 1.13 版本开始&#xff0c;coreDNS 就取代了 kube dns 成为了内置的 DNS 服务器。 Cor…...

c++ 连sqlserver

//要在 C 中连接 SQL Server 数据库&#xff0c;可以使用 Microsoft 提供的 SQL Server Native Client 或者 //ODBC 驱动程序。以下是使用 SQL Server Native Client 连接数据库的基本步骤&#xff1a; //1. 安装 SQL Server Native Client 驱动程序。 //2. 在 C 代码中包含头…...

给钉钉的2个建议

1. 建议.MD文件可以实现在线编辑 .MD文件可以实现在线编辑。 现状&#xff1a;word、excel、txt等文件都可以实现在线编辑&#xff0c;期望.MD文件也可以进行在线编辑&#xff0c;便于喜欢用.MD文旦交流的人使用。 2. 增加群内根据关键词自定义提醒功能 随着个人加入的群聊增多…...

STL之优先级队列(堆)的模拟实现与仿函数(8千字长文详解!)

STL之优先级队列&#xff08;堆&#xff09;的模拟实现与仿函数 文章目录 STL之优先级队列&#xff08;堆&#xff09;的模拟实现与仿函数优先级队列的概念priority_queue的接口介绍优先级队列的构造函数 priority_queue模拟实现类成员构造函数向下调整算法——正常实现 push向…...

设施管理系统

随着经济的快速发展&#xff0c;各种基础设施都在更新&#xff0c;在企事业单位中各种设施也都难以管理&#xff0c;以往传统的管理模式已经无法适应现代社会的需求&#xff0c;设备管理的滞后反而会影响设施设备的使用效果&#xff0c;因此设施设备管理系统必不可少。那么什么…...

JavaScript:获取当前日期、星期、时间 | Data对象

文章目录 1 Date对象2 代码示例3 获取 yyyy-MM-dd 格式的日期 1 Date对象 JavaScript 中的 Date 对象表示日期和时间。Date 对象基于自 1970 年 1 月 1 日 00:00:00 UTC&#xff08;协调世界时&#xff09;以来的毫秒数。以下是 Date 对象的一些常用方法和属性。 getFullYear…...

Cadence原理图快速查找元器件的方法

1.Cadence原理图快速查找元器件的方法 ①在红框中输入元器件编号&#xff0c;点击望远镜的图标在底下的状态栏可看到查找到的相关元器件&#xff0c;点击元器件可自动定位当前元器件的位置。 ②点击hierarchy&#xff08;层&#xff09;可自主查找&#xff0c;找到后点击序号即…...

科目二 调整座椅

靠背倾角 座椅高低 座椅前后用手抬起座椅前的横杠&#xff0c;让座椅向后移动方便上车 靠背左侧&#xff0c;向后扳扳杠调整倾角 座椅左侧&#xff0c;上下扳动调整高低头顶距车顶有一拳的距离 座椅前横杠一只手提起横杠另一只手握住方向盘前拉、后推调整到合适位置&#xff0c…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...