MySQL迁移GoldenDB避坑指南:自增参数与分片键配置详解
上个月帮一家公司做从 MySQL 到 GoldenDB 的迁移业务代码一行没动上线后第一波写入就把主键干撞了。排查了两个小时最后发现原因小得让人想骂人——自增参数没改。单机 MySQL 下从来没有出现过的隐藏假设到了分布式环境里全暴露出来了。今天把这次迁移中所有和参数相关的坑整理出来。如果你也准备把业务从 MySQL 迁到 GoldenDB这篇文章能帮你少走几周的弯路。文章里会重点讲一个迁移前必须改的参数组合也会把我在压测和现网验证阶段踩过的其他坑一并列出来。1. GoldenDB 与 MySQL 的兼容到底兼容到什么程度1.1 GoldenDB 是什么它和单机 MySQL 架构上差在哪GoldenDB 是腾讯基于 MySQL 内核开发的分布式数据库对外协议、SQL 语法、连接方式都兼容 MySQL。应用层只要把 JDBC 驱动换掉、连接串指向 GoldenDB 的接入地址绝大多数 SQL 不用改就能跑。这确实是它最大的卖点也是很多团队敢接这个迁移项目的原因。但协议兼容不代表运行行为完全一致。GoldenDB 的典型架构里包含三类角色计算节点CNCompute Node负责接收应用请求做 SQL 解析、优化、路由分发不存数据。数据节点DNData Node真正存储分片数据的 MySQL 实例每个 DN 都是一套完整 MySQL 引擎。全局事务管理GTM与管理节点负责全局事务分配、分布式一致性控制以及集群拓扑管理。我这次迁移的集群是标准三节点部署也就是三个数据节点。生产库从单机 MySQL 5.7 迁过去第一反应是既然语法兼容那配置应该也能照搬吧。这个想法就是后面事故的根源。你说数据访问层代码没换、SQL 习惯了单机 MySQL 那套行为模式但 GoldenDB 内部有三个 DN 在同时承担写入。单机 MySQL 里由单个实例保证的语义在分布式架构里需要多个节点协调。而一部分协调逻辑恰恰是通过参数开关暴露出来的。1.2 协议兼容不等于参数兼容先搞清楚哪些参数会被放大效应影响MySQL 单机版的参数作用范围默认就是当前这一个实例。到了 GoldenDB同类参数要作用在多个 DN 上。如果按单机习惯只改 CN 或者只改其中一个 DN大概率会出现集群内各节点行为不一致的诡异故障。举个例子lower_case_table_names这个参数。单机 MySQL 下影响的就是你这一台机器上的表名大小写匹配规则。GoldenDB 集群里CN 负责解析路由DN 负责实际存储如果 CN 的规则和 DN 不一致或者三个 DN 之间不一致就会出现表建成功了但在某个节点上找不到这种让人抓狂的错误。还有一类参数会直接决定 SQL 的执行计划。单机 MySQL 中一条不带 WHERE 条件的查询顶多就是全表扫描。但在 GoldenDB 里没有分片键条件的查询会广播到所有分片一台库变三台库慢查询的影响面直接乘三。这类行为差异如果没有对应的参数提前约束迁移后第一波压测就会现原形。所以迁移前花时间做一次 MySQL 参数基线与 GoldenDB 目标参数的逐项对齐比先跑数据再调试快得多也比想象中重要得多。2. 这个参数不改分布式自增 ID 就会给你上眼药2.1 单机 MySQL 自增逻辑与三节点写入的冲突根源单机 MySQL 里一张自增主键表的 ID 由单实例的计数器维护。AUTO_INCREMENT从 1 开始每插一条加 1绝对连续、绝对唯一。你的业务可能已经习惯了把这个自增 ID 当成业务实体标识甚至当成排序依据、对外单据号。GoldenDB 三节点部署下所有 DN 都接收写入。每个 DN 都是一个独立 MySQL 实例各自维护自己的AUTO_INCREMENT计数器。默认情况下每个 DN 都从 1 开始。假如你只写计算节点 CN由 CN 把 INSERT 分发到两个 DN其中一个 DN 产生 ID1另一个 DN 也产生 ID1那么全局主键就冲突了。我当时遇到的现象非常典型压测刚开始页面正常写入量一起来日志里疯狂报Duplicate entry 1001 for key PRIMARY。单看任何一个 DN 的数据文件ID 都是连续的、不重复的把三个 DN 的数据合起来看重复的 ID 到处都是。这就是分布式环境下局部唯一、全局不唯一的典型案例。解决思路也明确让每个 DN 的自增步长不再永远是 1而是错开增长区间。2.2 auto_increment_increment / auto_increment_offset 的正确设法MySQL 原生提供两个参数就是专门解决这种多主同时写入自增冲突问题的auto_increment_increment自增步长。auto_increment_offset起始偏移量表示每个实例从哪个数开始。单机 MySQL 下这两个参数默认都是 1也基本没人动。到了 GoldenDB 三节点环境必须按节点数调整。以三个 DN 为例数据节点auto_increment_incrementauto_increment_offset生成的 ID 序列DN1311, 4, 7, 10...DN2322, 5, 8, 11...DN3333, 6, 9, 12...这样三个 DN 生成的 ID 永远不会撞车。核心逻辑就是步长等于数据节点总数偏移量等于节点编号。三节点就把increment设为 3offset分别设为 1、2、3五节点就设 5offset 1 到 5。配置位置很关键要在每个数据节点的 MySQL 配置文件里改不是改计算节点# 数据节点 DN1 的 my.cnf [mysqld] auto_increment_increment3 auto_increment_offset1DN2、DN3 的配置除了 offset 不同其他保持一致。改完记得重启数据节点实例然后分别登录每个 DN 执行SHOW VARIABLES LIKE auto_increment%;确认三个节点分别输出offset1/2/3、increment3。2.3 迁移中如何验证 ID 全局唯一且趋势有序参数设完之后不能光看不测。我的验证方法非常土但有效三个 DN 都建一张测试表t_sys_inc_test(id BIGINT PRIMARY KEY AUTO_INCREMENT, node_flag VARCHAR(32))。程序直连 GoldenDB 的接入层并发向这张表写入 3000 条数据。写入完成后执行SELECT MAX(id), COUNT(*), COUNT(DISTINCT id) FROM t_sys_inc_test;。如果COUNT(id)等于COUNT(DISTINCT id)说明全局没有重复。再按 ID 分组看看分布是否均匀能直观看到三个 DN 是否真的在交替出数。这里有一个很多人会忽略的细节GoldenDB 若提供了全局自增序列能力业务侧可以不必依赖 MySQL 原生自增参数。但现实是很多业务代码直接用了 MyBatis 的useGeneratedKeys底层就是 MySQL 自增语义。迁移时最稳妥的方案是在数据节点层面把自增参数改到位这样应用层零改造风险最小。至于担心ID 不再连续的问题我的态度是分布式系统里自增 ID 连续本来就不该成为业务依赖。如果你有业务把自增 ID 当连续编号展示给用户趁迁移的机会尽早换成业务号生成策略别在这上面较劲。3. 除了自增参数迁移前还要对齐这批基础参数3.1 sql_mode、字符集、时区三个最容易忽略的隐性差异sql_mode是我排查过的另一个高频问题。它直接影响 SQL 行为的严格程度。MySQL 5.7 之后默认STRICT_TRANS_TABLES但老库可能是从 5.6 升级来的sql_mode被改得比较松。迁到 GoldenDB 后建议两边都保持严格模式否则会出现源库能插、目标库报错或者反过来源库报错、目标库默默截断这种对不齐的脏数据问题。字符集更麻烦。源库用了utf8mb4_unicode_ciGoldenDB 默认可能不完全是这套。迁移后一旦发生隐式转换索引就废了本来走范围扫描的 SQL 全会变成全表扫描。我习惯的做法是迁移前就在源库统一确认SHOW VARIABLES LIKE character_set_server; SHOW VARIABLES LIKE collation_server;然后把 GoldenDB 的每个 DN 都调成一致。数据库连接串上的characterEncoding也要确认是utf8mb4。这一步不花几分钟但漏掉之后查乱码、查索引失效会非常痛苦。时区参数time_zone同样要两边对齐。源库是08:00GoldenDB 如果设置成SYSTEM且系统时区是 UTC那么NOW()、CURRENT_TIMESTAMP返回的时间就会差 8 小时。对于按时间分表、按时间做统计的业务这种看起来没报错但数据全错的问题最难排查。3.2 lower_case_table_names大小写规则不一致会直接导致表找不到lower_case_table_names参数取值有三档取值行为适用场景0区分表名大小写存储和比较都敏感Linux 默认1表名转小写存储比较时忽略大小写Windows2按原样存储比较时转小写macOS单机 MySQL 在 Linux 上一般默认 0。如果你原来的表都是大写命名GoldenDB 某台节点上被设成 1那么所有对该表的 DML 操作都可能报Table doesnt exist。我的排查经验是这类报错通常只在特定节点上出现往往被误判为数据同步问题耽误很久。GoldenDB 官方文档会给出推荐取值但更关键的是迁移前后保持一致。源库是什么值GoldenDB 的 CN、DN 就都要设成什么值。别只改一台要改全集群。3.3 常用参数对照表MySQL 原值与 GoldenDB 建议值下面这份表是这次迁移中我逐项核对过的参数清单可以直接当模板用参数单机 MySQL 常见配置GoldenDB 三节点建议值备注auto_increment_increment13 DN 数量每个 DN 都设auto_increment_offset11/2/3按 DN 序号每个 DN 不同sql_modeSTRICT_TRANS_TABLESSTRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION两边必须一致lower_case_table_names0 或 1与源库一致全集群一致character_set_serverutf8mb4utf8mb4全集群一致collation_serverutf8mb4_unicode_ci与源库一致避免隐式转换time_zone08:00与源库一致建议写死时区max_allowed_packet4M 起按源库设置大字段同步依赖它wait_timeout默认值建议 3600防止迁移后连接频繁断开参数对齐做完迁移的数据管道搭建才有意义。参数都错了数据同步得越快错误扩散得越快。4. 分片键是 GoldenDB 的命根子相关参数得按路由规则来调4.1 分片键路由逻辑为什么有些 SQL 会变成全分片扫描GoldenDB 的分布式表创建时必须指定分片键shard key比如CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT NOT NULL, user_name VARCHAR(64) ) SHARDKEY(tenant_id);SHARDKEY(tenant_id)的意思是按tenant_id的哈希值把数据分布到不同的 DN 上。查询时如果 WHERE 条件里带tenant_idCN 就能直接算出来数据在哪个 DN一次请求只打一个节点。但如果 WHERE 条件里没有分片键CN 不知道数据在哪只能把 SQL 广播给所有 DN 执行然后做结果聚合。迁移初期这种广播 SQL 往往比源库慢好几倍。因为它不是慢在一台库上而是三台库同时扛还要额外做一次网络汇总。我迁移时踩过最典型的场景就是一张订单表按order_id分片但后台管理页面经常按user_id查订单。这种查询完全绕开了分片键变成了全分片扫描。压测时这几个查询一上来集群整体吞吐直接垮掉。4.2 是否强制带分片键这个开关怎么权衡GoldenDB 提供了一个控制选项决定是否允许执行不带分片键的 SQL。具体参数名在不同版本里可能不一样常见的是带shard_key字样的开关。取值一般有两种关闭宽松模式允许不带分片键的查询由 CN 广播执行。业务适配成本最低但存在慢查询拖垮全集群的风险。开启强制模式没有分片键条件的 SQL 直接报错拒执行。性能可控但对开发团队的要求更高所有查询条件设计都得先想到分片键。我的建议是分阶段走。迁移初期先开宽松模式让业务先跑通别一上来就报错把开发团队吓住。跑通后统计审计日志里的广播 SQL逐条优化确认没有热点之后再把开关收紧到强制模式。倒过来的话迁移当天所有没做分片键适配的 SQL 全报错压力会集中爆发。4.3 从 MySQL 单表改成分布式分片表的建表改造建议源库的单表没有分片键概念迁过来时分片键选得合不合理直接决定未来两三年你运维得顺不顺。我的经验是优先选这几类字段租户 ID / 企业 ID / 用户 ID 这类天然带有访问隔离属性的字段。经常出现在 WHERE 等值条件的字段。值分布足够均匀的字段避免数据倾斜。还有几个不能踩的坑不要选自增主键 ID 作为分片键除非你对按 ID 做等值查询的占比非常有把握不要选时间字段做分片键容易导致老数据集中在部分节点新数据又集中冲向新节点热点明显不能把分片键字段频繁更新否则 GoldenDB 的数据重分布成本会超出你的预期。建表迁移时还要注意原 MySQL 表如果是按时间归档的迁到 GoldenDB 后建议设计成分区表配合分片键而不是简单搬一张大表过来。分片维度管横向分布分区维度管纵向裁剪两者搭配才能压住大表膨胀。5. 一套可以直接抄的迁移参数模板与迁移后验证清单5.1 源端 MySQL 参数取值清单迁移前我建议先把源库参数导出一份生成基线文件方便迁后做偏差对比。导出的方式很简单SHOW VARIABLES;拿到完整结果后重点抓取以下参数写入迁移文档[mysqld] port3306 character_set_serverutf8mb4 collation_serverutf8mb4_unicode_ci lower_case_table_names0 sql_modeSTRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION time_zone08:00 max_allowed_packet64M wait_timeout3600 max_connections2000同步配置工具迁移时很多 DTS 工具只搬数据不搬参数所以这份基线必须人工维护好。别相信同步完就万事大吉的说法参数差异永远要从源库采集、目标端核对两边都做到有据可查。5.2 GoldenDB 目标端参数配置示例下面是我这次迁移最终落在生产环境的配置片段三节点的写法保持一致只有auto_increment_offset不同# GoldenDB DN 节点 my.cnf 公共部分 [mysqld] server_id1001 log_bin/data/mysql-bin binlog_formatrow character_set_serverutf8mb4 collation_serverutf8mb4_unicode_ci lower_case_table_names0 sql_modeSTRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION time_zone08:00 max_allowed_packet64M wait_timeout3600 # 分布式自增关键参数 auto_increment_increment3 # DN1 用 auto_increment_offset1 # DN2 用 auto_increment_offset2 # DN3 用 auto_increment_offset3计算节点 CN 的配置更关注内存、并发、超时类参数比如max_connections、interactive_timeout以及连接池相关的阈值。GoldenDB 官方安装文档里对这些有明确建议不要自己拍脑袋。5.3 迁移后 5 分钟提前验证法数据迁移完成后别急着切流量先跑一遍下面这套验证能提前暴露至少三分之二的参数问题分别登录每个 DN执行SHOW VARIABLES LIKE auto_increment%;确认三个节点的步长和 offset 全部正确。在三张分片表上分别插入几条记录用应用账号在连接层查询确认无主键冲突、无字符集乱码。执行SELECT sql_mode, lower_case_table_names, character_set_server, time_zone;和源库基线比对确认两边一致。找一条你知道会走分片键的等值查询用EXPLAIN看执行计划确认路由到了单个 DN 而不是广播到所有节点。用 DBeaver 或命令行工具连接 GoldenDB 接入层模拟应用典型的读写链路确认连接池、用户名权限、SSL 配置都正常。这套验证我每次迁移都跑基本能在业务真正接入之前把参数问题全部拦下来。DBeaver 连 GoldenDB 时如果遇到连不上先查 CN 的访问白名单和驱动版本很多报错其实是工具层面的兼容问题不是参数问题。6. 迁移实测中的三个典型事故复盘6.1 事故一ID 重复导致的主键冲突自增参数没改现象压测一上来日志报Duplicate entry集中在 PRIMARY KEY 冲突。数据量越大越频繁。排查链路先看应用日志确认报错来自哪张表再登录三个 DN 分别查这张表的最大 ID发现三个节点的 ID 明显重叠最后回到每个 DN 看自增参数发现三台全部是默认值increment1, offset1。根因就是分布式环境下每个 DN 独立生成相同序列。修复方法参考第 2 章的设置改完参数后重启节点并重建压测数据从头验证。6.2 事故二不带分片键的 SQL 打爆所有节点强制开关没开现象迁移后某个后台页面查询一次要好几秒而且一查整个集群 CPU 抖动其他业务跟着变卡。排查链路先看 CN 的慢查询日志确认 SQL 语句再看执行计划发现Extra字段里有广播聚合相关信息说明 CN 把查询发到了所有 DN然后统计这类 SQL 的调用频次确认来自后台管理端未适配分片键的接口。修复方案分两步短期内先从代码层面给查询补上分片键条件或者把高频查询改成按分片键路由中期再把分片键强制检查的开关打开从源头阻断新的广播 SQL 继续上线。6.3 事故三大小写敏感导致 Mapper 找不到表lower_case_table_names 不一致现象应用日志报Table db_xxx.T_User doesnt exist但用数据库客户端登录库看表明明建好了。排查链路这个问题的迷惑性很强因为客户端直连同一个接入地址能查到表应用却报找不到。后来排查发现应用连的账号走了不同的路由而其中一台 DN 的lower_case_table_names被设成了 1把大小写混合的表名统一转成了小写导致应用按照原始大小写名称访问时在部分节点上找不到表。修复方案比较简单全集群所有 CN、DN 统一设置lower_case_table_names与源库保持一致然后重启实例。但排查过程很折磨人所以我的建议是提前做集群级参数校验不要在故障发生后再去逐台比对。回看这次迁移最大的体会就是MySQL 生态的数据库迁移最坑人的往往不是数据本身而是那些你默认应该一样的参数。单机环境下参数只影响一台机器分布式环境下一个参数没对齐影响面就是全集群。如果你正好也在做类似的迁移别急着全量同步数据先花半天时间把这几类参数逐项核对一遍。特别是那对自增参数等它出来给你上眼药的时候损失的可不只是压测那点时间。