一. 背景
在日常工作中添加大表字段是个正常不过的操作,偶然发现团队中发现同事大量使用 ALGORITHM = INSTANT 更新字段,根据固有的理解,字段的更新必然会涉及到表结构的更改,印象中数据库会加入MDL锁去保证表数据的一致性。
但是听说在Mysql8.0+特性中,表明在更新字段的时候此方法不会导致锁表,因为这是一个在线的DDL的操作方式。后续带着疑问查询了大量的文章未果,依然没有理解其中的原理,固写下文研究过程。
二. 疑问点
例如下面的DDL方式
sql
ALTER TABLE users ADD COLUMN age INT DEFAULT 0 COMMENT '年龄', ALGORITHM=INSTANT;
在结尾处使用了【ALGORITHM=INSTANT】的字段表述方式,即可避免锁表。
orz. 是什么东西,难不成是传说中的"多级&叠加表"设计?我猜想:数据依然是老数据,但是新表是新表,所以查询引擎层会append一些新表结构的字段回去。
上面颠覆了我的认知。orz. 接下来的研究验证了我的猜想。
三. 研究过程
我查询了许多文章的表述,但是最终得到的更多是【Mysql8.0+特性】。显然这样的结论并不能直接说服我们对技术的渴望,偶然看到一篇帖子,恍然大悟。
记录在淘宝的【数据库内核技术2020年3月】的报刊中,我们发现研究者对这项特性是这样描述的:
在实现上,MySQL并没有在系统表中记录多个版本的schema,而是非常取巧的扩展了存储格式。在已有的info bits区域和新增的字段数量区域记录了instant column信息,instant add column之前的数据不做任何修改,之后的数据按照新格式存储。同时在系统表的private_data字段存储了instant column的默认值信息。查询时,读出的老记录只需要增加instant column默认值,新记录则按照新的存储格式进行解析,做到了新老格式的兼容。当然,这种实现方式带来的限制就是只能顺序加字段。
看完后,总结以下几点
- 在原有的数据表结构和新增的字段记录了【instant column】字段信息。
- 旧数据查询的时候会补充【instant column】默认值,新数据按新的结构存储。
对于大佬的总结,是根据一篇mysql的官网技术worklog来进行解读的
该技术第五点有个这样的阐述
sql
For "old" rows, the default value will be looked up from the new system
tables and appended before return to server.
翻译过来就是:【对于"旧"行,将从新系统中查找默认值表格并在返回服务器之前附加】
这句话验证了我的猜想:表里存在的旧数据,在数据返回到服务器将新的字段进行append回去,去展示对应的完整数据行。
那也就是我们在操作数据的时候,使用了ALGORITHM = INSTANT在表里使用了DDL的方式操作,那么我们将不会关注元数据的更改情况,也就是不用关心锁表。
但是也提供了【INPLACE&FORCE 】原来的复制表的方式来更新内部表,也就是锁表的方式。
关于Instant算法技术内幕原理
对于instant算法,可看上图
假设本身存在的一张表t1 , 先存在 a字段,插入一条数据x,此时存在了x(1) 【1 = 当前的列号情况】
现在ADD COLUMN更新表增加了b字段,再次插入一条数据y,此时存在数据x(1 + 1') + y(2)。【其中1' = append 1列的意思,同下】最新数据y将使用所有列的情况,而x旧数据只保存了a列的字段,并没有b的,所以需要组装返回
再次ADD COLUMN更新表增加了c字段,再次插入一条数据z,此时存在的数据x(1+2')+y(2+1')+z(3)。新增了c字段后,同上步骤,此时x将append 【bc列】的组装返回,y将append【c列】,而x会使用所有列的情况。
表达的方式类似如图最后一张。
学习到其他大佬的对sql执行过程输出,观察到在使用instant字段后对其表进行数据新增,其新增数据会在bit字段设置为1来代表数据是instant之后的。
因为对于内部执行流程 rec_set_instant_flag_new函数在记录的Info bits字段设置,REC_INFO_INSTANT_FLAG,会表示这个记录是instant add column之后创建的。
使用的注意点
- ALGORITHM = INSTANT 不能与 LOCK 子句一起使用,如果指定了 INSTANT,并且 LOCK=NONE/SHARED/EXCLUSIVE如果同时指定了 LOCK=DEFAULT,则会引发 ER_WRONG_USAGE 错误。但是,如果同时指定了 LOCK=DEFAULT,则可以。
- ADD COLUMN 可能会立即完成,因此我们可能不会期望立即进行 ADDCOLUMN 去修复损坏的索引
- 此工作日志不支持具有全文索引的表。
- 对于 EXCHANGE PARTITION,为了简化逻辑,如果分区或要交换的表是即时的,那么该操作将被拒绝错误 ER_PARTITION_EXCHANGE_DIFFERENT_OPTION。
- 对于使用它进行索引的创建或者更新,算法会被降级【ALGORITHM=INPLACE】,此时涉及到锁表,进行数据文件的物理修改。
- INPLACE 算法原理(GPT):
- 扫描表数据,构建索引结构。
- 允许并发 DML 操作(如 INSERT/UPDATE/DELETE),但某些阶段可能短暂阻塞 DDL。
- 最终替换旧表文件,完成索引创建。
四. 总结
使用Instant字段对元数据的操作(增加/修改字段和注释等),会将旧数据新增的字段列号只能附加在表的最后,而新数据将使用新表的数据方式存储。这是一个技术突破的东西。
仔细一看,mysql的描述感谢了Tencent的付出,腾讯哥还是厉害。他们主要的优化点是【ALTER TABLE .. ADD COLUMN 可以立即完成,不需要太多的 IO 和等待时间】。
这个特性的诞生,意味着大家对于新增字段大表的数据结构需要去增加字段,不会像之前那样操作锁表,mysql8.0+版本随意放心食用。