MySQL跨库迁移中TEXT与VARCHAR(65535)被视为不兼容,因物理存储机制不同(行内vs外存),影响索引、排序等性能;字符集、校对规则、SQL模式、生成列、NULL处理及pt-table-sync分块策略等隐式行为差异更易引发静默错误。
MySQL跨库迁移时,TEXT 和 VARCHAR(65535) 为什么总被当成不兼容?
因为 mysql 的跨库(比如从 mysql 5.7 迁到 8.0,或从阿里云 rds 迁到自建库)不校验语义一致性,只比对物理类型和长度。哪怕源表是 varchar(65535)、目标表是 text,看起来都能存同样多字符,但 mysql 内部处理方式完全不同:varchar 存在行内,受 innodb_page_size 和行格式限制;text 强制外存,影响索引、排序、临时表性能。
- 显式指定
CAST(... AS CHAR)或CONVERT(... USING utf8mb4)可绕过隐式转换失败,但不能解决排序规则不一致引发的Illegal mix of collations - 用
SHOW CREATE TABLE对比两边字段定义,重点看CHARACTER SET和COLLATE是否完全一致,哪怕只是utf8mb4_0900_as_csvsutf8mb4_unicode_ci都会触发报错 - 如果目标库是 MySQL 8.0+,而源库是 5.7,注意
TINYTEXT/MEDIUMTEXT在 8.0 中默认使用utf8mb4,但 5.7 可能是utf8,直接INSERT ... SELECT会因字符集降级失败
用 mysqldump 跨库导出时,--compatible 参数到底该设成什么?
--compatible 不是“让两边兼容”,而是“让导出的 SQL 尽量不带新版本语法”。设错反而导致字段类型被强制降级(比如把 JSON 列转成 LONGTEXT),丢失校验能力。
- 迁移目标是 MySQL 8.0,就别加
--compatible=mysql40或--compatible=ansi,它们会把ENUM拆成字符串、把SET转成CHAR,彻底丢弃语义 - 真正该加的是
--skip-extended-insert(方便 diff) +--default-character-set=utf8mb4(避免导出时乱码),再配合--set-gtid-purged=OFF(跨版本/非主从场景必关) - 如果源库有生成列(
GENERATED ALWAYS AS),mysqldump默认不导出其定义,需额外加--column-statistics=0并手动补ALTER TABLE ... ADD COLUMN ... STORED
写 INSERT ... SELECT 做异构字段映射,NULL 和空字符串怎么不踩坑?
MySQL 对 '' 和 NULL 的判断逻辑在不同 SQL_MODE 下差异极大。比如 STRICT_TRANS_TABLES 开启时,往 NOT NULL 的 DATE 字段插 '' 直接报错;而关掉它,会静默转成 '0000-00-00' —— 这个值在 8.0 默认被禁用,导致迁移中断。
- 统一在会话开头执行
SET sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE',让两边行为一致 - 对可能为空的字符串字段,显式写
NULLIF(trim(col), '');对时间字段,用NULLIF(str_to_date(col, '%Y-%m-%d'), '0000-00-00')避免非法日期 - 目标表字段若定义了
DEFAULT CURRENT_TIMESTAMP,但源数据里对应列为NULL,MySQL 会自动填当前时间 —— 这不是 bug,是预期行为,得提前确认业务是否接受
用 pt-table-sync 做增量校验,为什么 --chunk-size 设太小反而同步更慢?
因为 pt-table-sync 的 chunk 是按主键范围切分的,--chunk-size 设太小(比如 100 行),会导致单次查询返回大量小结果集,网络往返和事务开销远超数据传输本身;设太大(比如 100 万)又容易锁表、OOM 或触发 max_allowed_packet 限制。
- 合理值取决于主键分布密度:如果主键是自增 ID,且每秒写入量不高,
--chunk-size=10000是较稳起点 - 务必加
--algorithms=Chunk(默认就是,但显式写更安心),禁用--algorithms=GroupBy(它会用GROUP BY聚合比对,对大表极慢且不准) - 校验前先在目标库
ANALYZE TABLE,否则pt-table-sync可能因统计信息过期而选错索引,把全表扫描当成分块扫描
字段类型映射最麻烦的从来不是长度或名字,而是隐式行为差异——比如 TIMESTAMP 自动更新、DECIMAL 的精度截断规则、甚至 ENUM 的空字符串兼容性。这些不会报错,但会让数据在某次查询或升级后突然“不对劲”。

















