mysql升级后需验证元数据、数据一致性、语义逻辑、约束索引四类检查点:先查mysql.user和版本确认升级真实完成;再用pt-table-checksum做块级比对;接着抽样核验金额、时间、json等字段语义;最后验证外键生效与索引命中。

MySQL升级或迁移后,数据“看起来在”,不等于“真的对”——行数一致但金额字段被截断、时间戳精度丢失、JSON解析失败、外键静默失效,这些才是上线后爆雷的典型原因。必须跳过“服务能连上”的幻觉,直奔可验证、可归因、可回溯的检查点。
先确认系统库和元数据是否真正就绪
很多问题根本没走到业务表,卡在系统层:mysql.user 表读不出来、information_schema 视图返回空、权限校验报错,都是 mysql_upgrade 没跑成功或失败的信号。
- 执行
SELECT COUNT(*) FROM mysql.user;—— 若报Table 'mysql.user' doesn't exist或返回 0 行(且确定有用户),说明元数据未升级完成 - MySQL 8.0.16+ 已弃用
mysql_upgrade,改由启动时自动执行;此时必须查错误日志,找Auto-upgrade complete或Failed to upgrade data dictionary - 运行
SELECT VERSION(), @@version_comment;,确认不是“假升级”:进程是 8.0,但实际连的是残留的 5.7 实例 - 检查
SHOW ENGINES;中InnoDB是否为DEFAULT且状态为YES;若显示DISABLED,大概率是配置里误加了skip_innodb
用 pt-table-checksum 做分块一致性比对(不是简单 COUNT)
pt-table-checksum 是唯一能在线、低侵入、定位到具体块级差异的工具。别用 SELECT COUNT(*) 或手写 MD5(CONCAT_WS()),它们对 NULL、TEXT 截断、排序规则变更极度敏感,一比就错,全是噪音。
- 必须满足四个前提:
binlog_format = ROW、校验用户有REPLICATION CLIENT和percona.checksums表的写权限、所有表有主键或唯一非空索引、从库Seconds_Behind_Master ≈ 0 - 最小安全命令示例:
pt-table-checksum \ --host=master_ip \ --user=check_user \ --password=xxx \ --databases=mydb \ --replicate=percona.checksums \ --chunk-size=1000 \ --no-check-binlog-format
- 结果看
percona.checksums表里的difference != 0的记录,它会告诉你哪张表、哪个主键范围块不一致,而不是整表“哈希不对” - 如果报
Column count mismatch,立刻SHOW CREATE TABLE对比源/目标,常见于TIMESTAMP默认值行为变化(5.7 vs 8.0)、JSON字段隐式转换
抽样比对关键表的聚合值与边界数据
自动化工具覆盖不了语义逻辑。比如 SUM(amount) 看似一致,但可能所有金额都被截断到小数点后一位;MAX(create_time) 相同,但时区处理导致实际时间偏移 8 小时。
- 对核心表执行三类比对:
SELECT COUNT(*), SUM(amount), AVG(score), MIN(id), MAX(id), COUNT(DISTINCT user_id) FROM orders;
—— 注意浮点字段用SUM而非AVG,避免精度干扰 - 人工抽取 ID 在边界(如最小、最大、中间值)和业务典型(如最近下单用户、高净值客户)的 5–10 条记录,
SELECT *逐字段比对,尤其关注:TIMESTAMP/DATETIME字段值、ENUM显示值、JSON字段能否被JSON_EXTRACT正确解析、NULLvs 空字符串处理 - 字符集相关陷阱:5.7 默认
utf8mb4_general_ci,8.0 改为utf8mb4_0900_as_cs,会导致ORDER BY结果顺序不同,进而影响mysqldump输出顺序,造成哈希误报
验证外键、索引是否真生效,而非仅“存在”
SHOW CREATE TABLE 显示外键定义完整,不代表它正在起作用;EXPLAIN 显示用了索引,不代表优化器真选对了执行路径。升级后约束可能被静默禁用,索引统计信息可能陈旧。
- 执行
SELECT @@foreign_key_checks;,确保返回1;若为0,需手动SET FOREIGN_KEY_CHECKS = 1;并检查错误日志是否有修复失败提示 - 对含外键的子表执行一次强制重建:
ALTER TABLE child_table ENGINE=InnoDB;—— 这会触发 InnoDB 校验引用完整性,失败会直接报错 - 用
EXPLAIN FORMAT=TREE(8.0+)跑真实业务查询,确认是否命中预期索引;若没命中,执行ANALYZE TABLE table_name;更新统计信息 - 测试外键约束行为:尝试插入一条违反外键的记录,应明确报
Cannot add or update a child row,而不是静默忽略或转成0
最容易被跳过的其实是时间字段默认值兼容性和 JSON 字段的解析行为——它们不会让服务挂掉,但会让某天凌晨的对账脚本跑出负数。别依赖“没报错就是对”,得拿真实业务数据去撞。











