MySQL到金仓:自增主键与序列改造全流程——AUTO_INCREMENT兼容、并发写入与回退实战

发布时间:2026/8/15 23:42:33
MySQL到金仓:自增主键与序列改造全流程——AUTO_INCREMENT兼容、并发写入与回退实战 文章目录每日一句正能量1. 背景与问题AUTO_INCREMENT迁移的核心是把“下一个ID由谁生成”迁过去2. 环境与数据先判断是“简单自增”还是“分布式编号”2.1 为什么 auto_increment_increment/offset 必须检查2.2 KingbaseES有两条主流落地路线方案AIdentity方案BSequence Default3. 复现过程最容易踩的五个坑3.1 坑一导入3亿历史ID后目标生成器仍从1开始3.2 坑二误以为显式插入历史ID会自动同步目标生成器3.3 坑三把主键是否连续当成验收指标3.4 坑四只测试单条INSERT不测试批量主键回传3.5 坑五源库用多写AUTO_INCREMENT目标却按单写设计4. 方案实施一套可执行的AUTO_INCREMENT迁移步骤4.1 第一步建立主键清单4.2 第二步处理 UNSIGNED4.3 第三步优先Identity映射4.4 第四步Sequence兜底方案4.5 第五步全量导数保留原ID4.6 第六步增量同步仍然传原ID4.7 第七步切流前二次校准生成器4.8 第八步并发回归单条 INSERT10/50/100并发回滚显式历史ID4.9 第九步批量INSERT单独验证4.10 第十步ON DUPLICATE KEY UPDATE一起扫描5. 结果对比验收必须同时看数据、生成器和应用5.1 历史数据5.2 外键和业务引用5.3 生成器安全距离5.4 应用主键回传5.5 示例回归结果模板5.6 性能指标6. 风险与复盘自增主键最危险的是“双主同时生成”6.1 风险一切换窗口两边都在自动生成ID6.2 风险二多库分片ID被误当单库AUTO_INCREMENT6.3 风险三Sequence CACHE造成空洞被误判数据丢失6.4 风险四历史高值异常6.5 风险五BIGINT UNSIGNED容量问题6.6 风险六LAST_INSERT_ID依赖被遗漏6.7 风险七Sequence权限遗漏回退方案回源之前也要重新校准AUTO_INCREMENT最终复盘附录 AMySQL源DDL附录 BKingbaseES Identity方案附录 CKingbaseES Sequence方案附录 D最低回归清单每日一句正能量“人生没有标准答案敢于重新开始的人永远自带光芒。”每个人都可以书写自己的答案。而“重新开始”的能力是一个人生命力最璀璨的证明。跌倒后能爬起归零后能重启这种勇气本身就是一种无法被忽视的光芒。主题AUTO_INCREMENT 兼容 / MySQL → KingbaseES / 互联网业务迁移重点DDL 转换、Identity/Sequence 选择、历史 ID 保留、并发写入、主键回传、回归测试、数据校验与回退适用场景订单、用户、支付流水、内容主表、互联网业务单库或分片库迁移。1. 背景与问题AUTO_INCREMENT迁移的核心是把“下一个ID由谁生成”迁过去MySQL 互联网业务里最常见的主键定义之一CREATETABLEbiz_order(order_idBIGINTNOTNULLAUTO_INCREMENT,...PRIMARYKEY(order_id));很多迁移方案会直接写AUTO_INCREMENT → Identity然后认为工作完成。但自增主键真正影响的不是一行 DDL而是一整条写入链路INSERT不传ID → 数据库生成ID → 驱动/ORM拿回ID → 子表/消息使用这个ID → CDC传播 → 下一条记录继续生成对互联网业务还可能存在auto_increment_increment auto_increment_offset 多主写入 分库分表 号段 雪花ID 外部ID服务因此迁移前必须先判断源系统到底是在使用 MySQL 的“单实例 AUTO_INCREMENT”还是借用了 AUTO_INCREMENT 做多写节点的号段隔离。MySQL 8.4 官方说明InnoDB 为 AUTO_INCREMENT 维护专门计数器不显式提供值时由计数器产生新值。如果显式插入的 ID 大于当前计数器后续计数器会被推进。MySQL 同时提供auto_increment_increment和auto_increment_offset可以用于多服务器生成互不冲突的自增值。所以迁移目标必须覆盖历史ID不变 目标新ID不冲突 应用主键回传正常 并发写入安全 多写架构语义不丢失 异常时能回退2. 环境与数据先判断是“简单自增”还是“分布式编号”示例环境源库MySQL 8.0/8.4 InnoDB 目标KingbaseES V9 业务互联网订单服务 单表数据约3亿 日新增300万 主键BIGINT AUTO_INCREMENT 写入方式JDBC/MyBatis源表CREATETABLEbiz_order(order_idBIGINTNOTNULLAUTO_INCREMENT,customer_idBIGINTNOTNULL,order_noVARCHAR(64)NOTNULL,amountDECIMAL(18,2)NOTNULL,created_atDATETIME(6)NOTNULL,PRIMARYKEY(order_id),UNIQUEKEYuk_order_no(order_no));迁移前至少记录当前MAX(order_id) AUTO_INCREMENT当前起点 数据类型是否UNSIGNED auto_increment_increment auto_increment_offset 主库数量 是否多主写 应用如何取得新ID 是否有显式插入ID逻辑2.1 为什么auto_increment_increment/offset必须检查MySQL 官方文档说明auto_increment_increment控制每次自增的步长auto_increment_offset控制起始偏移。比如两个写节点节点Aoffset1, increment2 → 1,3,5,7... 节点Boffset2, increment2 → 2,4,6,8...如果这种架构迁移到 KingbaseES 后简单变成所有节点共用一个 START 1 INCREMENT 1虽然不会一定产生重复但源系统的分布式编号策略已经改变。如果应用或分片逻辑依赖id % 2判断来源节点就会出现业务问题。因此要先做“主键架构识别”。2.2 KingbaseES有两条主流落地路线当前 KingbaseES SQL 参考明确支持GENERATED ALWAYSASIDENTITYGENERATEDBYDEFAULTASIDENTITYIdentity 列会绑定一个隐式序列新插入行可以自动获得值。另外 KingbaseES 还提供独立CREATESEQUENCE支持START WITH INCREMENT BY CACHE NO CYCLE NEXTVAL CURRVAL SETVAL因此最常用两条路线方案AIdentityorder_idBIGINTGENERATEDBYDEFAULTASIDENTITY适合单写服务 传统AUTO_INCREMENT表 希望DDL简单方案BSequence DefaultCREATESEQUENCE biz_order_id_seq;order_idBIGINTDEFAULTNEXTVAL(biz_order_id_seq)适合需要显式管理生成器 需要更清楚控制START/INCREMENT/CACHE 迁移工具对Identity支持一般3. 复现过程最容易踩的五个坑3.1 坑一导入3亿历史ID后目标生成器仍从1开始源端MAX(order_id)386,120,008全量迁移order_id原值全部保留目标数据检查COUNT一致 MAX一致如果 Identity/Sequence 仍然从1开始切流后就有主键冲突风险。因此切换前必须满足目标NEXT VALUE 全局已使用MAX(id)这应该是自动化阻断条件而不是检查清单里“人工看一眼”。3.2 坑二误以为显式插入历史ID会自动同步目标生成器MySQL 的行为很容易形成惯性。MySQL 官方示例说明如果向 AUTO_INCREMENT 列显式写一个较大的值例如100随后自动生成的值可以从101继续。因此 DBA 容易形成“我已经导入了历史ID目标自增肯定也知道最大值。”但 KingbaseES 的 Identity 依赖隐式序列BY DEFAULT允许显式历史值优先不代表迁移程序就可以省略生成器校准。正确做法仍然是导完数据 → SELECT MAX(id) → 检查Identity/Sequence当前状态 → 显式RESTART/SETVAL/ALTER3.3 坑三把主键是否连续当成验收指标MySQL AUTO_INCREMENT 在事务回滚 并发插入 失败插入 批量语句场景下并不应该被当作业务连续号码。KingbaseES Sequence 也一样。官方开发规范明确建议不要将商业逻辑建立在序列完全连续性上并说明增大 CACHE 可以减少争用但会增加不连续的可能。所以验收重点是唯一 不会回退到已使用范围 并发安全不是1001、1002、1003一个都不能少订单号、发票号如果要求特定连续规则应使用独立业务编号机制。3.4 坑四只测试单条INSERT不测试批量主键回传MySQL 生态中很多框架依赖LAST_INSERT_ID() getGeneratedKeys() useGeneratedKeysMySQL 官方文档也明确提供LAST_INSERT_ID()/mysql_insert_id()获取最近自动生成的 AUTO_INCREMENT 值。迁到 KingbaseES 后数据库生成 ID 没问题不代表JDBC MyBatis JPA 批量INSERT都能用完全相同方式拿到主键。尤其批量插入INSERTINTO...VALUES(...),(...),(...);不能只假设拿到第一个ID → 后面的ID必然连续推导因为这种假设把“生成器连续性”当成了应用协议。应该让真实驱动/ORM返回并验证每个生成主键。3.5 坑五源库用多写AUTO_INCREMENT目标却按单写设计MySQL FAQ 明确说明MySQL 本身没有通用 Sequence但可以通过auto_increment_increment auto_increment_offset在多服务器场景减少 AUTO_INCREMENT 冲突。如果源系统双主 多源复制 多机房迁移时必须回答目标还是多写吗如果目标变成单主写可以把主键生成收敛到单一 Sequence/Identity。如果目标仍然需要多节点独立生成ID则要重新设计不同START/OFFSET的Sequence 号段 全局ID服务 雪花ID不能只把源表 DDL 翻译一下。4. 方案实施一套可执行的AUTO_INCREMENT迁移步骤4.1 第一步建立主键清单建议 SQL 清单至少记录schema table column type unsigned current_max_id auto_increment increment offset foreign_key_count write_qps id_generation_mode分类S1单库单写AUTO_INCREMENT S2多写increment/offset S3分库分表号段 S4外部ID/雪花 S5业务显式赋ID优先迁S1复杂度最低。4.2 第二步处理 UNSIGNEDMySQL 常见BIGINTUNSIGNEDAUTO_INCREMENT这不仅是自增问题也是数据类型范围问题。目标 KingbaseES 如果采用有符号BIGINT必须检查MAX(id)是否已经超过目标类型上限。大部分业务实际值远低于上限但迁移评估不能靠猜。必须MAX(id) 未来增长年限做容量评估。4.3 第三步优先Identity映射典型目标CREATETABLEbiz_order(order_idBIGINTGENERATEDBYDEFAULTASIDENTITY(STARTWITH1INCREMENTBY1)PRIMARYKEY,customer_idBIGINTNOTNULL,order_noVARCHAR(64)NOTNULLUNIQUE,amountNUMERIC(18,2)NOTNULL,created_atTIMESTAMP(6)NOTNULL);为什么使用BY DEFAULT而不是迁移阶段直接ALWAYSKingbaseES 官方语义是BY DEFAULT 用户显式提供值时用户值优先 ALWAYS 默认强制使用生成值除非显式覆盖系统值迁移全量和增量都需要保留 MySQL 历史 ID因此BY DEFAULT更方便。切换完成后是否调整更严格的写入策略可以通过应用层禁止传ID 权限 SQL审计实现。4.4 第四步Sequence兜底方案如果想把生成器独立出来CREATESEQUENCE biz_order_id_seqASBIGINTSTARTWITH386120009INCREMENTBY1CACHE100NOCYCLE;列order_idBIGINTDEFAULTNEXTVAL(biz_order_id_seq)KingbaseES 官方序列文档明确支持START WITH、INCREMENT BY、CACHE、NO CYCLE并可通过nextval/currval/setval管理生成器。Sequence 方案的工程优势生成器独立可见 参数更容易审计 多表/特殊生成策略可重用 迁移脚本容易校准缺点DDL比Identity多一个对象 权限要单独确认 对象命名和生命周期要治理4.5 第五步全量导数保留原ID历史订单1 2 ... 386120008目标必须仍然是1 2 ... 386120008不要重新生成。原因order_detail.order_id payment.order_id message业务键 数据仓库引用 日志关联都可能已经依赖原ID。如果重新编号就会把“主表迁移”变成“全链路主键重映射项目”。4.6 第六步增量同步仍然传原ID全量迁移运行几个小时甚至几天时MySQL 仍有新订单写入。CDC 增量INSERT order_id386120009目标也要INSERT order_id386120009而不是由 KingbaseES 自己生成一个 ID。迁移期MySQL是主键权威切流后KingbaseES才成为新的主键权威这个切换点必须明确。4.7 第七步切流前二次校准生成器第一次全量导完MAX(id)386120008几小时后 CDC 已经追到386520100所以不能只在全量后校准一次。切流正确顺序停止源端新写 ↓ 追平最后CDC ↓ 源目标MAX(id)比对 ↓ 目标生成器再次校准 ↓ 验证NEXT VALUE安全 ↓ 开启KingbaseES写4.8 第八步并发回归至少测试单条 INSERTINSERTINTObiz_order(customer_id,order_no,amount)VALUES(...);断言生成ID非NULL 数据库行ID 应用拿到ID10/50/100并发检查duplicate0 PK conflict0 generated ids unique回滚BEGIN INSERT ROLLBACK允许ID出现空洞但后续不得产生重复。显式历史ID分别插入比当前MAX低 比当前MAX高确认迁移流程和后续校准脚本都能正确处理。4.9 第九步批量INSERT单独验证MySQL 应用很喜欢INSERTINTOt(...)VALUES(...),(...),(...);迁移后至少验证生成多少个ID 驱动返回多少个 顺序是否对应输入行 批次部分失败如何处理不要用first_id i推算后续主键作为正式设计。4.10 第十步ON DUPLICATE KEY UPDATE一起扫描互联网 MySQL 常见INSERT...ONDUPLICATEKEYUPDATE...MySQL 官方文档说明它遇到 UNIQUE/PRIMARY KEY 冲突时可以转为 UPDATE在含 AUTO_INCREMENT 的表上它还会影响自增值和LAST_INSERT_ID()相关行为。因此迁移自增主键时应该顺便扫描ON DUPLICATE KEY UPDATE REPLACE INTO INSERT IGNORE LAST_INSERT_ID这些都属于“写入语义”不能只改表结构。5. 结果对比验收必须同时看数据、生成器和应用5.1 历史数据至少比较COUNT(*)MIN(id)MAX(id)COUNT(DISTINCTid)断言COUNT COUNT(DISTINCT id)主键无重复。5.2 外键和业务引用例如SELECTCOUNT(*)FROMorder_detail dLEFTJOINbiz_order oONd.order_ido.order_idWHEREo.order_idISNULL;结果05.3 生成器安全距离迁移完成MAX(id)386520100目标下一值必须386520100如果采用多节点号段还要验证所有节点未来生成空间互不冲突5.4 应用主键回传测试JDBC getGeneratedKeys MyBatis useGeneratedKeys JPA GeneratedValue 批量Insert 事务Insert必须断言应用对象ID 数据库实际ID5.5 示例回归结果模板用例MySQLKingbaseES结果单条自动ID成功成功通过显式历史ID保留保留通过50并发无重复无重复通过ROLLBACK后继续插入允许空洞允许空洞通过JDBC取主键正常正常通过批量Insert返回ID已验证返回通过这些是验收模板不是本文声称的真实生产结果。5.6 性能指标自增迁移还应该记录Insert TPS P50/P95/P99 生成器等待 WAL 索引写入 Sequence CACHE大小KingbaseES 官方开发规范建议适当增大 Sequence CACHE 可以降低争用但同时会增加序列不连续性。互联网高并发业务可以测试CACHE 1 CACHE 20 CACHE 100 CACHE 300选择性能和可接受空洞之间的平衡。再次强调主键唯一远比主键连续重要。6. 风险与复盘自增主键最危险的是“双主同时生成”6.1 风险一切换窗口两边都在自动生成ID这是最大的风险。如果MySQL继续AUTO_INCREMENT KingbaseES Identity也开放两个库可能在相同数值空间生成新ID。所以切换要保证同一个业务主键空间在任意时刻只能有一个权威生成器除非已经设计了明确不冲突的号段。6.2 风险二多库分片ID被误当单库AUTO_INCREMENT比如db0 → 奇数 db1 → 偶数或者每个分片从不同亿级号段开始这些信息可能根本不在表 DDL 中而在MySQL系统变量 部署配置 中间件 应用代码迁移清单必须覆盖数据库外部。6.3 风险三Sequence CACHE造成空洞被误判数据丢失KingbaseES 官方文档说明缓存序列号能提升性能但实例异常关闭时缓存中尚未使用的值可能被跳过。这不是订单丢失。真正的订单完整性应该通过业务唯一键 记录数 状态 消息链路判断。不要用ID必须连续做数据完整性校验。6.4 风险四历史高值异常可能绝大多数id 4亿但曾经人工修复id9,000,000,000如果只按“正常增长趋势”设置目标序列4亿1最终仍会撞到历史记录。所以必须读取真实MAX(id)6.5 风险五BIGINT UNSIGNED容量问题如果 MySQL 使用BIGINT UNSIGNED目标类型范围可能不同。即使当前数据没超过范围也要评估未来3年/5年增长不能等迁移后几年才发现主键逼近上限。6.6 风险六LAST_INSERT_ID依赖被遗漏应用可能直接执行SELECTLAST_INSERT_ID();MySQL 官方说明LAST_INSERT_ID()对当前连接生成的 AUTO_INCREMENT 值有明确语义。迁移后不能假设这个函数和连接态语义仍然完全一样。更推荐应用通过目标驱动支持的generated keys RETURNING获取新主键并做真实连接池测试。6.7 风险七Sequence权限遗漏如果用Sequence Default应用账号除了表 INSERT 权限还需要确认能够正常访问相关序列。这种权限问题往往在DBA账号测试成功 生产应用账号失败时才暴露。切换清单要使用真实应用账号执行。回退方案回源之前也要重新校准AUTO_INCREMENT假设MySQL最后ID 386520100切到 KingbaseES 后又写了50000条最大 ID 已经386570100现在因为应用兼容问题需要回退 MySQL。不能简单把连接串切回MySQL否则 MySQL 原计数器可能继续从386520101生成而 KingbaseES 窗口期已经使用了这些 ID。正确回退1. 停止KingbaseES新写 2. 固化目标最后ID和业务水位 3. 将目标新增记录反向同步MySQL并保留原ID 4. 校验两端MAX(id) 5. 将MySQL AUTO_INCREMENT推进到全局MAX(id)安全步长 6. 恢复MySQL写入口 7. KingbaseES转为只读排障回退原则和切流原则其实完全一致下一任主库的主键生成器必须位于所有已使用ID之后。最终复盘MySQL 到 KingbaseES 的 AUTO_INCREMENT 迁移建议按五层处理第一层识别源编号架构 AUTO_INCREMENT / incrementoffset / 分片 / 外部ID 第二层选择目标生成器 Identity / Sequence / 外部ID服务 第三层保留历史主键 全量 CDC 都显式传原ID 第四层校准生成器 NEXT VALUE 全局MAX(id) 第五层回归与回退 并发写 主键回传 双端水位 回源校准如果只记住一句话AUTO_INCREMENT 迁移不是把关键字换掉而是在迁移“谁拥有下一个唯一ID的生成权”。这个生成权只要在切换窗口中模糊一秒就可能留下后续非常难修复的主键冲突。附录 AMySQL源DDLCREATETABLEbiz_order(order_idBIGINTNOTNULLAUTO_INCREMENT,customer_idBIGINTNOTNULL,order_noVARCHAR(64)NOTNULL,amountDECIMAL(18,2)NOTNULL,PRIMARYKEY(order_id));附录 BKingbaseES Identity方案CREATETABLEbiz_order(order_idBIGINTGENERATEDBYDEFAULTASIDENTITY(STARTWITH1INCREMENTBY1)PRIMARYKEY,customer_idBIGINTNOTNULL,order_noVARCHAR(64)NOTNULL,amountNUMERIC(18,2)NOTNULL);附录 CKingbaseES Sequence方案CREATESEQUENCE biz_order_id_seqASBIGINTSTARTWITH386520101INCREMENTBY1CACHE100NOCYCLE;CREATETABLEbiz_order(order_idBIGINTPRIMARYKEYDEFAULTNEXTVAL(biz_order_id_seq),...);附录 D最低回归清单[ ] AUTO_INCREMENT列已全部扫描 [ ] 当前MAX(id)已记录 [ ] increment/offset已记录 [ ] UNSIGNED范围已评估 [ ] 分库分表/外部ID逻辑已识别 [ ] 全量导入保留原ID [ ] CDC保留原ID [ ] 目标生成器已二次校准 [ ] 单条INSERT主键回传通过 [ ] 批量INSERT主键回传通过 [ ] 10/50/100并发无重复 [ ] 回滚后写入通过 [ ] 应用真实账号权限通过 [ ] 回退AUTO_INCREMENT校准脚本已演练转载自https://blog.csdn.net/u014727709/article/details/163728579欢迎 点赞✍评论⭐收藏欢迎指正