MySQL 8.0 迁移到自建库那晚,我被一个 `utf8mb3` 的幽灵表搞得差点回滚

站长杂谈 23 浏览 0 回复 返回上级

上周把跑了三年的项目从阿里云 RDS 迁到自建服务器,导数据、配主从、切流量,流程走得挺顺。结果第二天早上一看监控,新库磁盘占用比原库多了 17%,而且持续增长。

排查半天,发现问题出在一个三年前建的日志表上。当时图省事,建表语句里写了 `DEFAULT CHARSET=utf8`,在 RDS 上实际落地是 `utf8mb3`。迁移时用 `mysqldump` 导出再导入,MySQL 8.0 直接给转成 `utf8mb4_0900_ai_ci` 了——字符集升级本身没问题,但那个表有个 `VARCHAR(255)` 的联合索引,字段字节上限从 255×3 变成 255×4,刚好踩中 3072 字节的索引长度限制,InnoDB 默默把索引拆成了前缀索引。

更坑的是查询计划变了。原库走覆盖索引的 SQL,新库开始回表,凌晨的定时统计任务从 40 秒涨到 8 分钟。我当时还在庆幸"迁移成功",其实是性能悬崖边蹦迪。

这次踩坑后我给自己定了三条铁律,分享出来:

一、迁移前先做 "字符集考古"

别信 `SHOW CREATE TABLE` 里看到的,去查 `information_schema.COLLATIONS` 和 `INNODB_SYS_TABLES`,确认每个表真正的字符集和行格式。MySQL 8.0 的 `utf8` 别名行为跟 5.7 完全不同,"看起来一样"是最危险的幻觉。

二、卸载旧环境时,先冻结再销毁

我原来的习惯是:新库验证通过 → 停旧库 → 删实例。现在改成了:新库验证通过 → 旧库改只读并保留 72 小时 → 切流量 → 观察两个完整业务周期 → 再降级销毁。那 17% 的磁盘差异就是第二天发现的,如果当时手快删了 RDS,回滚成本直接翻十倍。

三、升级脚本必须带 "降级探针"

这次迁移我写了自动化脚本跑结构变更,但只做了 "升级成功" 的判断。现在每个 `ALTER` 后面都跟着兼容性检查:索引长度、分区策略、外键约束名是否变化。脚本不只要跑通,还要能证明 "跑通后跟原来等价"。

最后说个细思极恐的:那个幽灵表在 RDS 上其实早该重构,但 `utf8mb3` 的隐性限制让它"看起来正常"了三年。迁移像一次体检,平时没症状的病,换环境全给你显形。

你们迁移时遇到过这种"沉默的字符集炸弹"吗?或者有没有那种"原库好好的,新库一跑就露馅"的玄学问题?

评论0
回复 · 0
还没有回复
微信客服 微信客服