600GB生产MariaDB库如何安全完成utf8到utf8mb4的转换
Debian Bullseye环境MariaDB 10.5 600GB生产库utf8转utf8mb4实操方案
MariaDB 10.5里原生的utf8类型本质是最大支持3字节的utf8mb3,转utf8mb4本身是向前兼容的,只要避开全表锁、隐式转码的坑,完全可以做到零数据损坏、最小业务影响。别直接在主库跑无锁配置的全量ALTER,600G体量下锁库时间动辄数小时,直接触发生产故障。下面是经过多套TB级生产库验证的落地方案:
第一阶段:测试环境全流程预验证(强制必做,不能跳过)
- 拉取生产最近一次全量物理备份恢复到测试环境,保证测试环境的OS版本、MariaDB版本、参数配置和生产完全一致
- 生成转换前数据基准:对所有表执行
CHECKSUM TABLE 库名.表名 EXTENDED;,记录每张表的行数、checksum值存为基准文件,不要用默认的快checksum,结果不准 - 按后续生产操作流程在测试环境跑完全部转换步骤,完成后重新对所有表做checksum比对,确认无数据不一致、无乱码,再拉业务侧做全功能回归,重点验证emoji、生僻汉字、4字节特殊符号的读写逻辑
- 记录测试环境每一步的操作耗时,给生产维护窗口留至少1.5倍的时间冗余,按普通SSD盘性能算,600G库全量转换大概需要2-3小时,SATA盘预留4-6小时足够
第二阶段:生产环境前置配置(全在线无锁,可提前3-5天操作)
- 修改MariaDB服务端配置,编辑配置文件
/etc/mysql/mariadb.conf.d/50-server.cnf,在[mysqld]段添加以下配置,改完采用滚动重启的方式生效(先逐台重启从库,最后重启主库),不会影响正常业务:
character-set-server = utf8mb4 # 排序规则必须和原有规则对齐,原库用utf8_general_ci就填utf8mb4_general_ci,原库用utf8_bin就填utf8mb4_bin,不要随意更换 collation-server = utf8mb4_general_ci init_connect = 'SET NAMES utf8mb4' skip-character-set-client-handshake = 1 # 索引兼容配置,MariaDB 10.5默认已开启,显式配置避免异常 innodb_file_per_table = ON innodb_large_prefix = ON
- 配置生效后执行
show variables like '%character%';确认所有服务端字符集参数已切换为utf8mb4,这一步只会让后续新建的表默认使用utf8mb4,不会修改存量表和已有数据,无业务风险 - 灰度修改业务侧数据库连接串的字符集配置为utf8mb4,观察业务读写无报错,这一步因为存量表还是3字节utf8,只要不写入4字节字符就完全兼容,不会出现异常
第三阶段:存量表字符集转换(低影响/零停机操作)
- 严禁直接执行
ALTER TABLE 表名 CONVERT TO CHARACTER SET utf8mb4;,该操作会持元数据锁全表锁写,600G库会直接堵死所有业务请求 - 优先使用percona-toolkit的
pt-online-schema-change工具逐表做在线转换,工具会通过触发器同步增量数据、无锁切表,不会阻塞业务读写,执行命令模板如下:
pt-online-schema-change \ --alter="CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" \ --host=127.0.0.1 --user=管理员账号 --password=账号密码 \ D=目标库名,t=待转换表名 \ --chunk-size=1000 \ --max-lag=5 \ # 主从延迟超过5秒自动暂停转换,避免影响从库承载的读业务 --max-load=Threads_running=50 \ # 数据库运行线程数超过阈值自动暂停,不抢占业务资源 --execute
- 转表顺序建议先转数据量小于1G的小表,最后转换核心业务大表,转换过程中全程监控主从延迟、数据库负载、慢查询,一旦出现异常直接终止工具即可,不会损坏原有数据
- 如果环境不允许安装第三方工具,采用从库转换+主从切换的零停机路径:选一台非核心从库停止SQL线程,在该从库上执行ALTER完成所有表的字符集转换,追平binlog和主库数据一致后,将业务流量切到该从库作为新主库,老主库完成字符集转换后重新挂载为从库即可,该方案适合对业务抖动零容忍的核心场景
第四阶段:收尾校验
- 所有表转换完成后,执行以下SQL排查是否存在遗漏的未转换表:
SELECT TABLE_NAME,TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = '目标库名' AND TABLE_COLLATION NOT LIKE 'utf8mb4%';
- 逐表执行EXTENDED级别的checksum,和转换前的基准值比对,确认数据完全一致,重点抽查存储emoji、生僻字、特殊符号的字段,确认无乱码、无内容截断
- 灰度观察1-2个业务周期,确认无字符集相关报错、无索引性能异常。网上流传的“varchar(255)要改成191才能用utf8mb4”的说法不适用于MariaDB 10.5,该版本开启innodb_large_prefix后单索引支持最大3072字节,varchar(255)转完后索引长度为1020字节,完全在限制范围内,不需要修改字段长度。
关键避坑事项
- 排序规则必须和原有库保持一致,随意更换排序规则会导致查询结果排序异常、已有索引失效
- 正式操作前必须做一次全量可恢复的物理备份,优先用mariabackup做热备,不要用mysqldump,600G体量下逻辑备份恢复耗时太长
- 不要认为修改服务端默认字符集就完成了转换,该操作不会修改存量表的字符集,后续写入4字节字符时依然会报截断错误
- 不用过度担心性能问题,utf8mb4和原3字节utf8在InnoDB引擎下的性能差异在5%以内,只要索引配置正确,不会出现明显性能下跌,部分场景因为消除了隐式转码,查询效率反而会提升
- 存储过程、触发器、视图这类对象的字符集不会随表转换自动更新,转完后需要逐个查看创建语句,确认字符集已切换为utf8mb4,避免后续调用时出现隐式转码报错
内容的提问来源于stack exchange,提问作者Henry A. Colby
相关产品推荐
相关产品推荐

