分批迁移不能直接用 TOP N + ORDER BY,因无稳定排序时行为不可控,易致重复或遗漏;须用唯一单调字段游标分页,如 WHERE id > @last_id ORDER BY id ASC。

分批迁移时为什么不能直接用 TOP N + ORDER BY?
因为 TOP N 在无稳定排序依据时行为不可控,尤其在并发写入或索引重建后,同一批数据可能被重复处理或跳过。必须用唯一、单调递增的字段(如自增 id 或 create_time + id 组合)做游标分页。
实操建议:
- 优先使用
WHERE id > @last_id ORDER BY id ASC拉取下一批,避免OFFSET导致的性能退化 - 若源表无合适主键,需先加
ROW_NUMBER() OVER (ORDER BY ...)临时编号,但注意该操作本身会阻塞大表 - 每批大小设为 1000–5000 行较稳妥;太大易锁表超时,太小则事务开销占比过高
如何让失败批次自动重试而不污染已提交数据?
关键在于把「迁移动作」和「状态记录」放在同一个事务里,并用独立的状态表标记每批的处理结果。不能依赖存储过程外的日志文件——宕机时无法回滚。
实操建议:
- 建一张
migration_batch_log表,含字段:batch_id(如'20240520_user_v2')、min_id、max_id、status('success'/'failed')、error_msg、retry_count - 每次执行前先查该批次是否已成功:若
status = 'success',直接CONTINUE - 捕获错误后,更新
status和error_msg,再THROW或RETURN,不隐式提交
TRY...CATCH 里怎么记录错误又不中断整个迁移流程?
SQL Server 的 TRY...CATCH 默认只捕获当前批,但若你在 CATCH 块里执行了 INSERT INTO error_log,而该语句又失败(比如日志表空间不足),会导致整个事务回滚并掩盖原始错误。必须隔离日志写入。
实操建议:
- 日志表用
INSERT INTO error_log WITH (TABLOCK)避免锁争用,且该表不参与主事务(用SET XACT_ABORT OFF+ 显式COMMIT) - 错误信息务必包含:
ERROR_NUMBER()、ERROR_MESSAGE()、ERROR_LINE()、当前@batch_id和@min_id - 不要在
CATCH中调用其他可能出错的存储过程,尤其是涉及链接服务器或外部 API 的
为什么迁移完成后还要校验行数和校验和?
分批提交不是原子操作,网络中断、触发器误删、权限变更都可能导致目标表少数据或多脏数据。仅靠「没报错」不等于迁移正确。
实操建议:
- 迁移前对源表跑
CHECKSUM_AGG(BINARY_CHECKSUM(*))(注意NULL和浮点数精度问题) - 迁移后对比目标表相同聚合值,再补查
COUNT(*)和关键字段的MIN/MAX是否越界 - 对敏感字段(如金额、状态)做抽样比对,例如随机取 100 条
id IN (SELECT TOP 100 id FROM source ORDER BY NEWID())
最常被忽略的是时间字段的时区转换和 datetime2 精度截断——哪怕看起来“没报错”,2024-05-20 14:30:45.1234567 写进 datetime 类型就只剩 .123。

















