前阵子刚把一家马来西亚游戏公司的核心交易库从 SQL Server 迁到了 OceanBase整个项目做完之后一直在陆陆续续收到同行问迁移细节。这个案例其实挺典型游戏业务增长快、日志表和流水表膨胀迅速、许可证成本压顶、还要考虑东南亚多地区的容灾和扩展。如果你也正面临类似的“要不要换库”“从 SQL Server 怎么迁到 OceanBase”的纠结这篇文章可以给你一条比较完整的参考路径。文章不会只讲“我们用了什么工具”而是把为什么要迁、迁之前怎么盘点存量对象、表结构怎么设计拆分键、全量增量同步怎么做、一致性校验怎么搞、上线切流怎么灰度、以及我们踩过的一些坑全都展开讲清楚。对于数据库这块同一个问题可能有一百种解法我分享的是目前被验证过可行的方案你拿去复现的时候根据自己的业务体量做加减法就行。1. 迁移前的项目背景为什么要动这套跑了好几年的 SQL Server1.1 这家马来西亚公司的真实痛点先交代一下这家公司的情况。团队规模不大核心是自研棋牌类社交游戏和几款休闲手游业务覆盖马来西亚本地市场也在往新加坡、印尼等东南亚国家扩展。原系统很纯粹应用层以 Java/Go 微服务为主数据层全部落在 SQL Server 上高峰期大概有几十个实例其中交易库和日志库占大头。真正让他们下决心迁移的是三个绕不开的问题。第一个是许可证成本。SQL Server 按核心授权收费随着业务增长每次扩容都要额外购买许可而且版本越高越贵。他们当时跑的是企业版光许可证摊销下来的年成本已经顶得上一个小团队的口粮。对于游戏公司来说这种固定成本是相当难受的毕竟游戏业务有明显的波峰波谷为了波峰时段去加买常驻许可太不划算了。第二个是扩展性瓶颈。SQL Server 的常用扩展路径无非是主从复制、读写分离、分库分表。前两者解决不了单库容量和写入吞吐的天花板分库分表则要业务代码大量改造而且分片规则一旦定死后面再扩容又是噩梦。他们最大的几张玩家流水表单表已经到了几亿行日增千万行索引重建和归档清理对线上影响越来越大。第三个是跨机房容灾和弹性扩张的需求。东南亚不同地区的玩家延迟敏感他们希望在多地域部署读写节点同时要求故障时能快速切换。SQL Server 的 AlwaysOn 可用性组功能虽然强大但配合多地域网络环境运维成本和复杂度都不小并且切换时的脑裂处理、见证服务器等一堆配置问题小团队根本撑不住。1.2 为什么选择 OceanBase 而不是继续升级 SQL Server在选型阶段他们其实也对比过 PostgreSQL、MySQL 分库分表方案、以及 OceanBase。最终选 OceanBase有几个决定性因素。最重要的一点OceanBase 的分布式架构是原生内置的不需要业务代码做分库分表。你仍然需要设计合理的拆分键但对应用层来说它看起来就是一个“超大号单库”连接串、事务、JOIN 都还是常规用法。这一点对比 MySQL 分库分表中间件方案能省掉大量业务改造。其次是兼容性。OceanBase 同时兼容 MySQL 和 Oracle 两种模式。对于原来跑 SQL Server 的系统绝大多数团队会选择切到 MySQL 兼容模式因为游戏后端团队对 MySQL 的熟练度普遍更高周边生态和运维工具也更熟悉。SQL Server 里的很多写法在 MySQL 模式下做一遍改造成本是可控的。再一个就是容灾能力。OceanBase 通过 Paxos 协议实现多副本强一致自动选主和故障切换配合全球部署能力可以做到同城三副本或跨地域多活。对东南亚这种跨国家、跨地区部署场景比手动维护 SQL Server 的可用性组轻松太多。还有一个容易忽略的点OceanBase 社区版对功能限制很少这解决了他们成本上的最后一块心病。没有了按核心授权的许可成本扩容改成加机器、加节点弹性也好了很多。1.3 迁移的核心认知这事本质上是一次“方言改写 架构升级”做完选型后团队对迁移难度有过几个误判这里先说清楚免得你走弯路。事故一以为 SQL Server 的 T-SQL 和 MySQL 语法差不远数据库迁移就是导数据。实际上存储过程、函数、触发器、视图的改写工作量才是大头。如果源库有大量复杂的 T-SQL 逻辑那迁移周期可能比预想的长得多。这家公司还好核心业务逻辑大部分在 Java 服务里数据库层的存储过程不算特别多但即便如此我们也花了大量时间做兼容性排查。事故二以为分布式数据库是把单机 Sql Server 表原样搬过去就行。实际上拆分键的选择直接决定后续性能和运维体验。比如玩家流水表如果按流水号哈希那按 player_id 查询就会变成跨节点扫描正确做法是跟玩家维度相关的表统一按 player_id 做拆分键并且放到同一个 TableGroup让关联查询尽可能在一个节点内完成。所以整个迁移项目的定位应该是先做一次彻底的存量 SQL 资产盘点再做方言转换然后才是数据搬迁和切流。数据搬运本身真的不难难的是把“旧思维下的数据库对象”翻译成“分布式体系下的最优形态”。2. 迁移前体检SQL Server 存量对象兼容性盘点动手迁之前我强烈建议先花一到两周做一次完整的“数据库体检”。目的是把所有数据库对象、数据量、增长趋势、依赖关系摸清楚然后生成一张兼容性改造清单。我们当时组织了一次类似“大规模代码审计”的动作把线上几十个库的所有表、视图、存储过程、函数、触发器、序列、作业脚本全部捞出来逐个比对。这样做的原因很简单分布式迁移最怕半路发现某个冷门存储过程里的语法无法转换或者某个表的数据类型在目标端被悄悄截断。提前暴露这些问题比迁到一半再返工要省太多时间。2.1 数据类型映射一张表搞定大部分工作量SQL Server 和 MySQL 模式的数据类型大体对应但细节差异不少稍不小心就会埋雷。这里给出一份我们自己整理并最终落地的映射对照表。SQL Server 类型OceanBase MySQL 模式类型注意事项intint直接映射注意自增列的种子值bigintbigint直接映射常用做主键tinyinttinyintSQL Server 的 tinyint 是 0-255MySQL 的一样注意无符号设置bittinyint(1)如果习惯布尔值用 tinyint(1) 即可查询层兼容decimal/numericdecimal精度需保持一致金额类字段建议统一 decimal(18,2) 以上moneydecimal(19,4)避免直接用浮点类型存钱smallmoneydecimal(10,4)同上按实际金额范围调整datetimedatetime精度上 SQL Server 约 3.33 毫秒OceanBase 可到微秒级无兼容问题datetime2datetime映射后注意精度提升可能影响应用层比较逻辑smalldatetimedatetime范围变大但取值行为需要回归测试datedate直接映射timetime直接映射注意精度和小数秒位数char/ncharchar/varchar注意字符集和排序规则尤其是定长字段的空格补齐行为varcharvarchar关键是长度单位SQL Server 按字符数MySQL 模式默认也按字符但要确认nvarcharvarchar统一使用 utf8mb4 字符集否则多语言字符可能乱码text/ntextlongtextSQL Server 的 text/ntext 是历史遗留迁移时顺便升级成 longtextvarbinaryvarbinary直接映射binarybinary直接映射uniqueidentifiervarchar(36)不建议用 char(36) 做主键会产生随机写热点后续详解sql_variant不支持需要业务层枚举或 JSON 化改造xml不支持建议序列化成 text/json由应用层解析geometry/geography不支持需要单独方案比如改用文本坐标或地理空间中间件这张表不能直接当成结论每个字段的真实取值范围需要在迁移前抽样确认。比如 SQL Server 里 nvarchar 存的表情符号等四字节字符到 OceanBase MySQL 模式如果字符集没设成 utf8mb4就会变成乱码或报“Incorrect string value”。我们当时最大的坑之一就是字符集后面单独讲。2.2 T-SQL 方言改造的常见差异数据类型映射是死板的语法改写才是迁移中真正吃时间的地方。SQL Server 用的是 T-SQLMySQL 模式是类 MySQL 方言很多高频写法都要改。我把我们整理过的高频差异列一下照着自己项目排查就可以了。功能场景SQL Server 写法OceanBase MySQL 模式写法获取当前时间GETDATE()、SYSDATETIME()NOW()、SYSDATE()日期加减DATEADD(day, 1, col)DATE_ADD(col, INTERVAL 1 DAY)日期差DATEDIFF(day, a, b)DATEDIFF(a, b) 注意参数顺序相反字符串拼接a bCONCAT(a, b) 或直接用空值判断ISNULL(a, b)IFNULL(a, b) 或 COALESCE(a, b)取前 N 条SELECT TOP 10LIMIT 10行号/排名ROW_NUMBER() OVER(...)ROW_NUMBER() OVER(...) 写法一致条件分支CASE WHEN ... THEN ... END一致存储过程定义CREATE PROCEDURE ... AS BEGIN ... ENDCREATE PROCEDURE ... BEGIN ... END注意 DELIMITER 处理变量声明DECLARE a INT; SELECT a 1DECLARE a INT; SET a 1临时表CREATE TABLE #temp建议用 CTE 或派生表或使用真实临时表但注意连接池分页OFFSET ... FETCHLIMIT ... OFFSET自增列IDENTITY(1,1)AUTO_INCREMENT全局唯一标识NEWID() / NEWSEQUENTIALID()UUID() 或应用层生成有序 UUID字符串长度LEN(col)CHAR_LENGTH(col) 或 LENGTH(col)后者按字节类型转换CAST(a AS INT)CAST(a AS SIGNED)还有一个细节SQL Server 的字符串拼接如果遇到 NULL整个结果是 NULL而 MySQL 模式下 CONCAT 函数会自动忽略 NULL。如果业务以前依赖了这个行为改造之后就可能会出现数据不一致的小 bug。这种隐性差异只有靠充分回归测试才能发现。2.3 字符集、排序规则与时区问题字符集这块海外游戏公司尤其要重视。SQL Server 里最常见的做法是用 nvarchar Latin1_General_CI_AS 排序规则来同时存英文、中文、马来文等字符。迁到 OceanBase MySQL 模式后统一方案是字符集 utf8mb4、排序规则 utf8mb4_general_ci 或 utf8mb4_0900_ai_ci。这里要注意三个实操细节。第一个是排序规则的大小写敏感问题。CI 是 case insensitive如果你的表某些字段原来是 CScase sensitive迁移到 CI 排序规则后查询条件里的字符串匹配行为会变化。比如原来区分“Abc”和“abc”的查询迁移后可能合并到一起。我们的做法是对每一个唯一索引、主键、以及频繁作为查询条件的字符串字段逐条确认是否需要区分大小写需要的话就在字段级指定 cs 排序规则。第二个是长度计算。SQL Server 的 varchar(n) 的 n 按字符数计算MySQL 模式下通过 CHARACTER SET 设置也基本按字符数但如果你在连接串里或者表定义里用了 BYTE 语义就需要注意。字符集和数据实际字节数的差异会影响索引前缀长度可能导致“Specified key was too long”之类的报错。第三个是时区。SQL Server 的 datetime 是不带时区信息的OceanBase 的 datetime 同样不受会话时区影响但 timestamp 类型会随会话时区变化。我们最终统一策略是所有业务时间字段优先用 datetime 类型应用层统一按 UTC8马来西亚时间写入和读取少数需要跨时区展示的字段用 timestamp 并在 JDBC 连接串里显式指定 serverTimezone。不要小看这个配置如果两边各写各的切流后玩家看到的活动开始时间可能差了好几个小时。3. 迁移方案设计全量 增量 灰度切换的三步走体检做完改造清单列出来之后就开始设计具体的迁移路径了。我们最终采用的是标准的三阶段方案全量数据迁移、增量日志同步、灰度切流。整个过程的目标是让业务中断时间控制在分钟级甚至在流量低峰期做到几乎没有感知。3.1 拆分键设计分布式数据库最关键的一步OceanBase 是分布式数据库表默认需要选定拆分键。拆分键选的合不合理直接决定了后续所有查询是“本地执行”还是“跨节点执行”。跨节点执行不是不行但涉及分布式事务和网络传输性能和稳定性都会打折。游戏场景里最自然的拆分维度就是 player_id。我们当时的表设计遵循了三条原则。第一条核心玩家维度表统一按 player_id 哈希。玩家表、玩家背包表、玩家邮件表、玩家成就表、玩家交易流水表全部以 player_id 为拆分键并且放在同一个 TableGroup 里。这样玩家在一局游戏里的背包变更、邮件领取、成就更新在同一个节点内部就能完成避免了大量跨节点分布式事务。第二条配置类、枚举类、全局小表设置为复制表。比如游戏版本配置、活动配置、服务器列表、道具字典这类“所有人都会读、几乎没人写”的表复制表会在每个节点都保留一份完整副本。玩家登录时要读一堆配置如果每次都是跨节点扫描并发一大就会卡死复制表完美化解这个问题。第三条日志类和流水类大表用“拆分键 时间分区”的组合。比如玩家邮件日志表拆分键仍然是 player_id但同时按发送时间做 Range 分区。这样既能支撑玩家维度的查询又能让归档和清理操作直接 drop 分区不再需要逐行 delete。这里还要特别提醒一下主键设计。如果原 SQL Server 表的主键是 uniqueidentifier也就是日常说的 GUID迁移的时候不要直接拿这个字段做拆分键。原因很简单GUID 是随机生成的哈希分布看起来均匀但写入时会产生大量随机主键索引页分裂和缓冲区污染而且游戏场景里很多查询是“查一个玩家的所有记录”用 GUID 做主键根本无法把同一个玩家的行聚在同一个节点。我们的做法是保留 GUID 作为业务标识字段但拆分键和主键统一用 player_id 或新增的自增 bigint 代理键。3.2 全量迁移怎么做才快全量迁移的目标是把 SQL Server 里已有的历史数据搬到 OceanBase。听起来简单但一次性搬几十个库、上百亿行数据一定要提前规划并行度和通道数。我们用的路径是结构迁移用官方工具做 DDL 转换和补充人工修正数据迁移通过迁移服务和 DataX 同步通道混跑。核心做法如下首先按表的数据量分梯队。玩家流水、充值流水这类超大表走分片并行拉取按照主键范围切成若干个段每个段由独立的同步线程负责并行度控制在 8 到 16。实测下来单表 14 亿行左右的流水在千兆内网环境下全量拉取差不多一个晚上能跑完。这里有个细节切分键一定要用主键不能随便找一个存在大量空值的字段否则容易出现某些分片跑很久、某些分片瞬间结束的严重倾斜。其次是参数调优。全量导入时批量写入的 batch size 很关键。我们试过从 100 到 1000 的若干档位最终稳定在 500 行左右一提交配合 16 并行整体吞吐最高且对源库压力可控。batch size 太小会频繁提交浪费网络 RTT太大则可能导致事务版本过多OceanBase 内存压力上升甚至触发大事务限制。再一个容易被忽略的点索引要在数据导入之后再建。结构迁移阶段只建主键和唯一键普通二级索引等数据导入完成后再通过并行 DDL 添加。否则一边写数据一边维护索引导入速度会慢好几倍。这一点跟单机数据库的做法反直觉但分布式数据库里大批量导入时索引维护的开销更明显。全量迁移过程中源库的业务不能停。为了让源库数据发生变化时能够持续同步我们同时开启了增量同步链路。这里就涉及到下一个阶段的细节。3.3 增量同步与延迟控制增量同步的原理本质上是持续读取 SQL Server 的事务日志或 CDC变更数据捕获记录然后把增删改操作实时回放到 OceanBase。我们从原来的 SQL Server 存量库开启 CDC再通过同步服务把变更流转到 OceanBase。这个方案的好处是源库不需要介入业务代码应用层完全不知道底层数据在复制。增量链路运行中最核心的监控指标就是同步延迟。正常运行时延迟应该稳定在 1-5 秒内如果出现分钟级延迟大概率是这几类情况某个超大事务一次性更新了上亿行同步通道跟不上源库日志文件暴涨CDC 读取被拖慢目标端 OceanBase 出现热点分片导致单节点写入压力过大。我们遇到过最严重的一次延迟是因为游戏运营团队在 SQL Server 上手动跑了一个大脚本一次性给全服玩家发邮件涉及几百万行插入结果增量延迟直接飙到了 15 分钟。解决方式比较粗暴给这种运维脚本设置分批提交每批限制一万行并且提前通知 DBA 关注同步延迟。如果你后续也会在源库跑批量脚本强烈建议也做同样的限制。另外增量同步阶段需要时刻关注主键冲突。如果全量迁移结束后源库还有少量“窗口期”数据未同步而目标端已经能写入就可能产生重复主键。我们在下文中会详细讲双写时的坑这里简单提一句进入切换阶段时一定要先停掉目标端的非同步 DML 写入或者应用层做开关控制。3.4 数据一致性校验与灰度切换数据搬过去和“数据真的对得上”是两回事。我们做了一整套校验流程核心是分段聚合校验加抽样抽查。分段聚合校验的思路特别简单但非常有效把一张大表按主键切成 N 个区间段对每个段分别计算 count、sum(关键数值列)、min(主键)、max(主键)在 SQL Server 和 OceanBase 两边各跑一遍然后比对结果。如果某个段对不上再单独拉出这个段里的数据做差集分析。这个做法的好处是避免全表一行行比对效率高而且能快速定位差异范围。抽样校验则侧重于字段内容本身。对每一张核心表按 player_id 随机抽取一部分样本把整行数据序列化成 JSON 后对比。重点考察字符串字段是否有乱码、金额字段精度是否丢失、时间字段时区是否正确。切流顺序也是门学问。我们分了四步走第一步切只读流量。先把内部报表平台、BI 数仓的数据读取切到 OceanBase 上持续运行三到五天验证读链路稳定性。第二步切部分边缘业务。例如游戏内公告查询、排行榜下载这类非核心读多写少的服务切到 OceanBase 上观察应用错误率和响应时间。第三步切核心写业务。选择凌晨低峰期对源库开启只读等待增量同步延迟追到 0然后把应用连接串从 SQL Server 切到 OceanBase。此时源库仍然保留只读方便回滚。第四步稳定观察 72 小时后再彻底断开源库的复制链路。这套流程虽然保守但对游戏公司来说是最稳妥的。回滚手段时刻都在只要源库只读一旦 OceanBase 侧出现严重问题把连接串切回去业务就能恢复。实际上我们最终没有用到回滚但预案完整带来的安全感能让整个团队在切流时保持冷静。4. 实战踩坑记录那些文档里不会写的细节这一章节是真正“用头发换来的经验”。很多问题如果不去实际迁移一遍光看官方文档是根本发现不了的。4.1 自增列和序列的迁移SQL Server 里最常见的自增列写法是 IDENTITY(1,1)OceanBase MySQL 模式对应 AUTO_INCREMENT。DDL 翻译很简单但有一个非常隐蔽的坑自增列的当前种子值。举个例子原 SQL Server 的表里玩家 ID 已经自增到了 9527但结构迁移只把表结构建出来了没有同步“当前种子值”目标端新写入的玩家 ID 可能从 1 开始。如果此时增量同步还在继续新玩家插入时就直接主键冲突了。所以全量迁移完成后必须立刻核对每一张自增表的最大 ID并把 AUTO_INCREMENT 初始值设置到 max(id)1。这一步我们在项目中写了自动化脚本来生成 ALTER TABLE 语句手工做几百张表很容易漏。如果你用的是序列对象而非自增列OceanBase 里也有 CREATE SEQUENCE 的语法支持但要注意下一值和缓存步长。SQL Server 老系统里手动管理序列的情况也很常见这种建议直接用应用层改造替代。4.2 触发器与存储过程的改造SQL Server 里的触发器在游戏数据库里非常常见比如玩家表插入后自动初始化背包、充值表插入后更新余额等。但分布式数据库架构下触发器是尽量少用的主要原因有两点一是触发器在分布式环境中容易引发隐式的跨节点事务出问题时定位困难二是一旦批量导入数据触发器会逐行执行导致导入性能断崖式下跌。我们的做法是逐条梳理触发器逻辑把能在应用层做的事情全部挪到应用层。比如“玩家表插入后初始化背包”这个动作改由微服务在业务代码里显式调用失败还可以重试日志也清晰。如果实在无法避免触发器也建议把触发逻辑简化成消息事件或异步任务千万不要把复杂的级联更新留到数据库里。存储过程的改造更是工程量集中地。这家公司有大概 300 多个存储过程其中一半以上涉及游标、动态 SQL、临时表。动态 SQL 在 MySQL 模式里用 PREPARE 语句可以实现但调试麻烦游标可以改写为 JOIN 或窗口函数临时表尽量改成 CTE 或派生表。每一个存储过程都要做“逐行级语义对比”压力测试后确认结果一致才能过。4.3 死锁与热点问题排查上线后遇到的第一个真实事故是某个活动开启瞬间大量玩家同时操作背包和货币资产出现了一批死锁和超时。原因是虽然我们把表拆分到了不同节点但同一个玩家的大并发更新仍然会在同一把行锁上竞争。而且这类活动通常有几个“全服唯一”的道具配置行所有玩家来修改这个配置行状态就形成了典型的热点行。排查与解决的路径是这样的通过 OceanBase 的日志和错误堆栈确认死锁涉及哪几张表、哪几个事务然后用事务重放工具模拟发现主要是事务更新顺序不一致导致的。比如事务 A 先更新货币表再更新背包表事务 B 先更新背包表再更新货币表两个事务在分布式锁下就容易互相等待。解决办法有两个一是调整应用代码让同一个玩家维度内的多张表更新严格按照固定顺序执行比如先背包后货币全团队统一二是把超高频更新的“库存扣减”改为乐观锁实现即 UPDATE 时带 version 条件返回 0 行则重试。实测下来死锁基本消失活动时段的错误率从千分之几降到了零。热点行的问题和表拆分键密切相关。如果一个“全服唯一”的配置项放在普通哈希表里那所有写都打到这一个节点要么用复制表让读取变快要么拆分成多个子配置项分散写。游戏秒杀、领奖这类逻辑尤其要注意建议压测阶段就要模拟全服并发的极限场景。4.4 连接池、驱动、时区等细碎问题这类问题单个看起来不大但每一条都会在某个时刻咬你一口。连接池SQL Server 的 JDBC 驱动和 MySQL 驱动的 URL 参数体系完全不同。切到 OceanBase 后用 HikariCP 或 Druid 的团队需要重写连接串并且把 maxActive 和 initialSize 调小一些。SQL Server 连接池通常开得比较大因为单机连接消耗可控而 OceanBase 是分布式架构连接实际分布在多台节点上连接数开得过大反而导致节点间握手和内存消耗增加。我们在生产上把核心服务的最大连接数从 200 降到了 50性能和稳定性反而更好。驱动版本一定要用与 OceanBase 版本配套的驱动包不要拿通用 MySQL 驱动的旧版裸连。曾经有一个服务因为驱动版本过老对 utf8mb4_0900_ai_ci 排序规则不识别导致查询直接报错。时区问题连接串里显式加 serverTimezoneAsia/Kuala_Lumpur不要依赖默认值。马来西亚是 UTC8如果服务器系统时间正好不是这个时区又没有显式指定时间字段就会偏差。SQL 超时SQL Server 默认允许长查询长时间运行OceanBase 下的分布式查询如果设计不好可能被超时机制打断。建议应用层超时阈值不要设置得太长同时利用 OceanBase 的 SQL 诊断视图定期找那些执行计划走坏的查询。5. 迁移完成后的稳定运行经验切流完成只是新阶段的开始。上线后的一个月内团队的主要工作变成了 SQL 性能治理和稳定性调优。如果你也准备迁移下面这些经验可以直接复用。5.1 上线初期的 SQL 性能治理刚刚切到 OceanBase 时由于执行计划管理和统计信息有一个收集和自适应过程一部分在 SQL Server 上跑得飞快的查询在 OceanBase 上的执行计划可能不走索引或产生不必要的分布式子计划。不要慌这是迁移初期的常见现象。我们的具体做法是上线前三天每天从 OceanBase 的 SQL 监控里拉取 Top N 慢 SQL逐一分析执行计划找出与 SQL Server 行为不一致的地方。常见的优化动作包括补充统计信息或手动收集。对高频查询调整索引利用 OceanBase 的局部索引和全局索引各自优势。玩家维度的查询尽量用局部索引即索引与拆分键保持一致如果不带拆分键的查询无法避免就考虑全局索引但要注意全局索引带来的写入放大。对应用层 SQL 做规范化禁用 SELECT *减少大字段返回。把原生 SQL 中针对 SQL Server 优化器写的 HINT 全部清理干净那些 HINT 在 OceanBase 里不仅无效还可能引发误解。压测时就要把慢 SQL 治理跑起来不要等上生产再打磨。5.2 常见问题速查表最后整理一份高频问题速查表方便你后续排查。这些来自我们项目里真实遇到过的场景每条都对应着一次真实的报警或人为排查。现象可能原因解决动作迁移后中文/表情符号乱码字符集不是 utf8mb4 或连接串字符集设置不对修改表字符集为 utf8mb4连接串加 characterEncodingutf8插入报主键冲突自增种子值未调整到 max(id)1逐表重置 AUTO_INCREMENT 初始值增量同步延迟飙升源库批量大事务、CDC 拉取积压大脚本分批提交同步通道扩容查询结果和原来不一致大小写敏感排序规则变化、时区偏差逐字段确认 collation 和字段类型分布式死锁多表更新顺序不一致统一应用层更新顺序或改乐观锁活动热点行超时单一配置行高并发写复制表/配置项拆分/缓存前置全量导入速度慢并行度低、batch size 不合理、索引过早创建分片并行、batch 500、后建二级索引查询不走索引统计信息缺失、拆分键不匹配收集统计信息检查拆分键与查询条件连接数耗尽连接池配置过大或泄漏调低连接池上限排查连接未释放存储过程改造后行为不一致游标、临时表、动态SQL的语义差异逐行对比结果尽量用 CTE 改写5.3 迁移过程中的项目管理和人员组织数据库迁移从来不只是 DBA 的事。我们的项目经理后来复盘时说这个项目真正的成本曲线是“前期 SQL 改造占 40%数据迁移占 20%联调和切流占 40%”。所以团队配置上至少要有三类角色懂 SQL Server 业务逻辑的开发、熟悉 OceanBase 运维的 DBA、以及能做全链路压测的测试工程师。每周的迁移例会只看三样东西兼容性改造清单完成率、全量/增量同步的延迟趋势、目标端的慢 SQL 数量。这三样都稳定下来了切流才有底气。不要用“感觉差不多了”来决定切换日一定要用数据和指标说话。有一点心得想特别分享迁移窗口的核心不是“数据搬得快”而是“尽可能缩短不可写的时间”。我们做到了分钟级靠的是前面全量和增量两个阶段铺垫得足够扎实切换时的动作其实就是把连接串改一下。6. 迁移后的运维机制与效果回顾项目上线至今运行了大半年回看这次从 SQL Server 到 OceanBase 的迁移最直观的收益是三个。一是许可证成本归零。以前花钱买许可的预算转化为机器成本而且机器是随着业务增长按需扩容的月底结算起来账很清晰不再有一次性的巨额支出。二是扩容变得平滑。东南亚新开服时直接在 OceanBase 集群里增加节点和租户资源不再需要预先估算 SQL Server 许可和单机规格。游戏上线活动需要临时加资源时几分钟就能完成操作这在以前根本做不到。三是容灾能力明显增强。多副本强一致和自动故障切换已经在一次机房网络抖动中被验证过应用层无感知玩家无感知。相比以前手动切换可用性组运维同学终于可以睡个安稳觉。如果现在让我重新做一次项目我会在前期兼容性盘点阶段就把“触发器改造”和“拆分键设计”这两件事提到最高优先级它们比数据搬运本身更容易决定项目的成败。对于准备动手的团队我的建议是先找一张最核心的表做一次端到端试点迁移从 DDL 转换跑通到增量同步、校验和切流模拟这一套流程完整走一遍之后再启动全量范围迁移能把整体风险降低一大截。
阅读完成 · 觉得有帮助?