最近做了一次 MySQL 到达梦数据库的 DTS 迁移,整体数据量比较大,迁移过程中遇到了连接数、大表读取、超时、自增列、重复约束以及超大表无主键等问题。
这里主要记录一下实际遇到的问题和排查思路,后面再碰到类似场景可以直接参考。
1. 迁移顺序
这次没有直接把所有对象一次性全部迁过去,而是大致按照下面的顺序处理:
text
表定义、必要的主键/唯一约束
↓
表数据
↓
普通索引、其他约束
↓
视图、存储过程等其他对象
↓
数据核对
大表比较多的情况下,不建议一开始就把所有普通索引都建好再导数据,否则数据写入过程中还需要持续维护索引,整体速度会受到影响。
MySQL 自带的系统库不在业务数据迁移范围内:
text
information_schema
mysql
performance_schema
sys
2. AUTO_INCREMENT 列迁移报错
遇到过一张表,MySQL 源端结构类似:
sql
id INT NOT NULL AUTO_INCREMENT
但是 id 并不是主键,只存在普通索引。
迁移表结构到达梦时出现:
text
AUTO_INCREMENT列必须为唯一性约束的一部分
这种情况下需要注意,源端能正常存在的表结构,不代表目标端可以直接按原定义创建。
这张表后面根据实际数据确认 id 没有重复,所以在达梦侧给该字段增加了唯一约束:
sql
ID INT AUTO_INCREMENT NOT NULL UNIQUE
再继续迁移数据。
这里不能看到自增列就直接补主键,还是要先确认源表实际约束和数据情况。
3. 主键和唯一约束重复
还有一种情况是:
text
PRIMARY KEY(ID)
已经创建完成,后续迁移约束时 DTS 又尝试创建:
text
UNIQUE(ID)
达梦会提示已经存在相同的唯一关键字或主键。
如果 UNIQUE 和现有主键的字段组合完全一致,可以不再重复创建。
但下面这种不能直接跳过:
text
PRIMARY KEY(ID)
UNIQUE(ID, CODE)
因为两者约束的字段组合并不相同。
4. DTS 导致 MySQL 连接数快速增加
迁移过程中碰到过:
text
Too many connections
可以先通过下面几个命令确认连接情况:
sql
SHOW FULL PROCESSLIST;
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
继续从系统连接情况排查后,发现 DTS 启动迁移时会快速建立多个到 MySQL 的连接。
所以这个问题不能只看 MySQL 当前有多少连接,还要继续确认到底是谁在创建连接。
实际处理时主要是降低 DTS 并发,例如:
text
表任务并发降低
并发导出关闭或调低
导入并发降低
避免大量任务同时启动
尤其源端本身性能一般时,把并发开得很高不一定更快,反而容易先把连接数和源端资源打满。
5. 大表迁移很慢,先判断到底是哪一端慢
这次迁移里有不少千万级、亿级大表。
源端主要通过:
sql
SHOW FULL PROCESSLIST;
观察当前 SQL 和执行状态。
目的端服务器使用:
bash
vmstat 1
重点关注:
text
us sy id wa st
现场排查时,达梦目的端 CPU 和 IO 整体压力并不明显,而 MySQL 源端部分大表即使没有明显其他查询负载,执行:
sql
SELECT COUNT(*) FROM big_table;
也可能接近一小时。
这种情况下,排查方向就应该优先放到源端大表扫描和数据读取,而不是一直盯着目标端写入。
后面迁移大表时,我一般会同时看:
text
DTS 当前进度
+
MySQL SHOW FULL PROCESSLIST
+
目的端 vmstat
这样比只看 DTS 界面要直观很多。
6. 超大表不建议开启"显示行数"
DTS 在分析对象时,如果开启表行数统计,可能需要到源端执行类似:
sql
COUNT(*)
对于普通表影响不大,但是碰到几亿行、又没有合适索引的 MySQL 表,单是统计行数就可能花很长时间。
所以超大表迁移时,如果没有必须提前知道精确行数,可以关闭这类行数统计,避免正式迁移还没开始,时间先耗在 COUNT(*) 上。
7. Read timed out
后期迁移超大表时,多次遇到:
text
Read timed out
结合 SHOW FULL PROCESSLIST 排查,发现大部分时间还是在等待源端 MySQL 返回数据。
大致可以理解为:
text
DTS/JDBC 发起查询
↓
MySQL 扫描超大表
↓
长时间无法及时返回数据
↓
连接等待时间过长
↓
Read timed out
所以看到这个报错以后,不要第一时间只检查达梦。
至少先同时确认:
sql
SHOW FULL PROCESSLIST;
以及目的端:
bash
vmstat 1
如果目标端比较空闲,而源端查询长时间处于执行状态,排查方向就应该优先转向 MySQL 服务器、网络以及连接超时。
8. DTS 显示已导出、已导入不一致
迁移大表时也碰到过:
text
已导出 > 已导入
缓存 = 0
并且界面短时间没有明显变化。
这个时候不一定代表任务已经卡死。
可能是当前批次正在目标端提交、处理事务,或者 DTS 内部正在做批次切换。
我的处理方式还是先看源端:
sql
SHOW FULL PROCESSLIST;
如果过一会源端查询重新出现,同时 DTS 的导出、导入数字继续增长,就继续等待,不要因为界面短时间没变化直接把任务停掉。
9. 无主键、无索引超大表怎么处理
这次最后碰到的一张表比较麻烦:
text
数据量预计亿级以上
无主键
无唯一键
无有效索引
这种表很难直接做稳定的断点分批。
一开始也考虑过:
sql
LIMIT offset, count
但这种方式对超大表并不理想。
一方面 OFFSET 越大,后面的查询成本越高;另一方面没有稳定排序键的情况下,用 LIMIT 做迁移断点也不够可靠。
后面采用的思路是建立一张迁移中间表:
text
原表
↓
中间表
+ __DTS_MIG_ID 自增主键
↓
DTS 分批查询
↓
达梦正式表
中间表增加:
sql
__DTS_MIG_ID BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY
然后通过:
sql
WHERE __DTS_MIG_ID BETWEEN 1 AND 10000000
进行分段。
下一批:
sql
WHERE __DTS_MIG_ID BETWEEN 10000001 AND 20000000
以此类推。
在 DTS 列映射时,不把 __DTS_MIG_ID 映射到达梦正式表,这样辅助字段只用于迁移分片,最终业务表结构仍然保持原样。
10. 中间表方案实际也遇到了问题
实际执行时,中间表本身创建成功,但是后面的:
sql
INSERT INTO 中间表
SELECT ...
FROM 原超大表;
运行较长时间后连接被释放,最终中间表仍然没有有效数据。
这个现象说明,中间表方案虽然可以解决后续分片问题,但是"如何把原超大表完整复制到中间表"本身也可能成为新的瓶颈。
尤其源表本身没有主键、索引和可靠的分段字段时,很难在不修改原表的前提下直接做稳定的断点复制。
目前这一步还需要继续从源端排查,重点看:
text
MySQL 错误日志
服务端连接超时相关配置
服务器 CPU / IO / 内存情况
网络连接情况
如果只是客户端看到 Read timed out,还不能直接判断到底是连接超时、MySQL 主动断开、服务器资源问题还是网络问题,最终还是要结合源端日志确认。
11. 迁移过程中源端仍有数据写入
这次还有一个比较现实的问题:迁移过程中源端业务仍然可能继续写入数据。
对于有主键、时间字段或者明确增量字段的表,后续还可以考虑补迁。
但对于:
text
无主键
无唯一键
无可靠时间字段
这种表,迁移完成以后很难准确判断哪些是新增、哪些被修改、哪些被删除。
如果要求最终数据严格一致,最稳妥的方式还是在切换阶段停止源端写入,再做最终迁移或核对。
12. 一些经验
这次最大的感受是,大数据量迁移不能只盯着 DTS 的进度条。
真正有用的还是把问题拆开看:
text
源端读得快不快
↓
网络传得稳不稳
↓
DTS 连接是否正常
↓
目的端写得动不动
如果目的端 CPU、IO 都比较空闲,而 MySQL 大表查询本身就很慢,那么继续调高 DTS 并发通常意义不大。
正式环境做类似大规模迁移时,最好提前确认:
text
源端服务器性能
源目的端网络链路
尽量减少中间网络转发
提前识别千万级、亿级大表
提前检查无主键、无索引表
确认迁移期间源端是否继续写入
很多迁移问题不一定是 DTS 本身的问题,而是到了大表阶段以后,把源端性能、表结构和网络问题一起放大了。
总结
这次迁移遇到的问题比较杂,但基本可以归成三类:
text
结构兼容问题
连接与超时问题
超大表性能和分批问题
小表迁移通常比较顺利,真正耗时间的还是千万级、亿级大表,特别是源端没有主键和有效索引的情况。
后续再做类似迁移,我会在正式开始前先把超大表、无主键表、源端连接数和网络链路单独列出来,提前做迁移方案,而不是等 DTS 跑到最后几张表再处理。