MySQL 案例:大表改列的新技巧(Generated Column)

时间:2022-07-22
本文章向大家介绍MySQL 案例:大表改列的新技巧(Generated Column),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

前言

作为一个 MySQL DBA,和大表打交道的次数想必不少,大表上的 ALTER 操作一般影响都很大,平时会用 Online DDL 工具来辅助操作,但是本文会介绍一种特殊的技巧来应对一部分大表上的 ALTER 需求。

解决方案

从标题可以看出来,这次会用到 MySQL 5.7 的新功能:Generated Column,这种虚拟列在添加的时候耗时在秒级以内,也不需要 rebuild 表,对磁盘空间和数据库服务器资源的压力几乎没有,在应对一些紧急情况和比较严峻的资源场景的时候偶尔会发挥出奇效~

案例 1

背景

业务的新需求,在超过 5000 万行的大表上需要调整一个有唯一索引的 VARCHAR 列,从大小写不敏感变为大小写敏感,且调整完之后仍旧需要唯一索引。

PS:实际案例,脱敏改造了一下。

简要分析

MySQL 在判断大小写是否敏感的时候,依据的是字符集的 collation 设置,默认情况下是大小写不敏感的。

前文的环境为例,加上唯一索引之后,再试试插入新数据:

测试效果

可以发现 Adam 和 adam 会被认为是相同的值,MySQL 的一致性校验会报错。

解决这个 collation 的办法还是不少的:SQL 加上 collation 的 keyword,视实际的业务场景评估 ALTER 操作的可能性等等。而 Generated Column 这个特性提供了另外一种解决思路:创建一个虚拟的列,把唯一索引设置在这个虚拟列上,然后业务 SQL 使用这个虚拟列来查询。

实践一下

在测试表上创建一个新的虚拟列,然后加上唯一索引。

ALTER TABLE stu ADD COLUMN cs_sname varchar(16) COLLATE utf8mb4_bin GENERATED ALWAYS AS (sname);
ALTER TABLE stu ADD UNIQUE unq_csname(cs_sname);

看看表结构:

表结构示例

这时候再插入一些数据,看看实际效果:

效果演示

可以看到,在不 rebuild 表,也不变更列属性的情况下,这个业务需求就已经实现了。如果 sname 这一列还需要继续使用的话,记得加上普通索引。

总结一下

通过一个 0.00 秒的 ALTER 语句,在无需额外磁盘空间,仅付出理论上少量的 CPU 算力的代价之下,这个大表上变更列的需求就这么解决了。相比较于耗时耗力的风险评估以及 Online DDL,这个新技巧体现出了巨大的有事。不过要特别注意一点,这种行为会带来一定的维护和理解成本,切忌滥用

案例 2

背景

虚构案例,函数索引

简要分析

MySQL 的功能性一直是饱受诟病,函数索引在其他 RDBMS 上都有支持,但是 MySQL 这边一直都得通过各种奇怪的手段来间接实现,或者是直接在代码层计算完之后再查询。在 MySQL 5.7 之后,利用 Generated Column 肯定是可以实现函数索引的:用函数计算的结果生成一个虚拟列,然后再使用虚拟列查询。

不过在这种常规的思路之外,其实 MySQL 还多做了“一点点的改进”。来简单实践一下,看看这个“改进”是什么。

实践一下

仍旧使用上文中的表,使用 mod 函数建立一个虚拟列:

ALTER TABLE stu ADD COLUMN num_mod int GENERATED ALWAYS AS (mod(total,10));

效果如下:

实际效果

所以按照常规的思路,业务查询在需要用到函数的时候,应该要使用虚拟列num_mod。例如:

select * from stu where num_mod=4
Explain 结果

那么“一点点改进”是什么呢?不需要数据库端做任何变动,改改 SQL 看看效果:

Explain 结果

可以看到 MySQL 已经可以直接识别到 where 条件中的函数,然后利用虚拟列的索引来执行查询,而不再需要专门修改 SQL 去使用虚拟列了。

总结一下

仅从效果来看,只需要隐藏虚拟列的存在,MySQL 基本也可以认为是“支持”函数索引了,不过可能 Oracle 比较懒,没有去做这个事情吧,只能靠 DBA 去手动支持一下了。

结语

Generated Column 可以随意自定义“规则”的特点给了它极大的可能性,在面对一些棘手的场景时,Generated Column 也许就是让难题迎刃而解的画龙点睛之笔。