Skip to content

分区、迁移、合服与下线 ​

Category ​

server / capacity 子树。节点定位:合服与迁移是不可逆的资产级操作——分区设计决定迁移成本、ID 撞号、引用完整性、映射表永久保留、停写干跑核对纪律。

Definition ​

合服和迁移是游戏生命周期里最危险的一类操作:平时跑得再稳的系统,在这些事件面前都要接受一次全面的拷问——数据归属清不清楚、ID 会不会撞、引用完不完整、退路存不存在。迁移事故和普通故障不一样,它往往不可逆,出一次就是玩家资产级别的事故。所以这一领域的核心功夫,几乎全在动手之前。

分区的初始设计决定了多年后迁移的成本,这是一个典型的“开始时看不见、结束时改不了”的决策。本篇从分区设计讲到合服执行,再到下线归档,贯穿的主线只有一个:让数据的身份和归属从第一天起就是清楚的。

Problem ​

迁移事故和普通故障不一样:它往往不可逆,出一次就是玩家资产级别的事故。ID 撞号是最典型的隐性冲突,引用完整性决定迁移质量,没有退路的迁移没有讨论价值——核心功夫几乎全在动手之前。

分区设计决定迁移成本 ​

数据从出生那天起带不带“属于哪个区服”的标记,决定了未来合服是搬运还是重构:

  • 数据自带区服维度:玩家 ID 里编码服号,或所有表带服号字段,合并时不同服的数据天然不冲突,合服退化为主要绕开重名问题的逻辑合并。
  • 数据只有全局自增身份:不同服的 ID 从同一个起点各自增长,合并时必然撞号,只能全量重映射。

前者的成本在系统设计期一次性付掉,后者的成本在每次合服时反复支付,而且伴随长期风险。这是容量与分区设计里回报最高的一个早期决策。已经身处后一种状态的项目,也仍然值得在下次扩容时补上区服维度,越晚补,存量数据越多,补的代价越大。

ID 撞号:最典型的隐性冲突 ​

合服的显性冲突容易处理,玩家重名、公会重名,加后缀或送改名即可。真正危险的是隐性冲突,其中最典型的就是 ID 撞号:

  • 两个服的玩家 ID 都是各自从某个起点自增的,合并后同一 ID 指向两个不同的人。
  • 撞号不像重名那样能被提前枚举发现,往往在合并执行时才暴露。
  • 处理方式通常是给其中一个服的 ID 整体加偏移,偏移量取目标服现有最大值加安全余量。

偏移重映射听起来只是加个数,实际意味着源服所有数据的主键和外键都要改写。这是一次波及全部数据表的手术,任何一个环节漏改,症状都不会立刻出现,而是以“邮件发给不存在的人”“好友列表里多了个陌生人”这类零星投诉的形式,在之后的几周里陆续冒头。

引用完整性决定迁移质量 ​

重映射的难点不在映射本身,而在引用完整性——所有引用旧 ID 的地方都要同步改写:

  • 直接引用:邮件收件人、好友关系、公会成员、师徒关系。
  • 间接引用:排行榜快照、订单记录、日志流水、客服工单。
  • 嵌在内容里的引用:公告正文、补偿名单、邮件文案里出现的玩家标识。
  • 自由文本里的引用:留言板、签名档这类玩家自填内容中提到的他人标识,几乎只能靠抽样和事后补偿兜底。

保证完整性靠的不是细心,而是机制。常见的做法是让所有数据访问经过统一的标识层,迁移时只改这一层和底层数据;迁移脚本以全量扫描的方式改写,而不是按记忆枚举表清单;改写完成后做行数与抽样比对,源侧各表行数之和与目标侧行数对得上才放行。漏改的本质原因几乎都是“存在一份没人知道的数据副本”,全量扫描是唯一能兜住它的手段。

映射表为什么值得永久保留 ​

迁移完成后,新旧 ID 的映射表看起来已经无用,实际上它是后续所有工作的翻译字典:

  • 客服工单和玩家投诉用的是旧 ID,处理时要翻译成新身份。
  • 历史日志和流水按旧 ID 记录,排查和审计都要回查。
  • 二次合服、数据修复、补偿发放,都依赖映射表定位历史归属。

映射表通常建议保留到产品生命周期结束。它不参与业务读写,占用有限,而一旦丢失,历史数据的解释权就永久失去了。很多团队吃过这个亏:第一次合服时按“临时数据”清理了映射,几年后处理历史纠纷时无从下手。

合服时机与前置盘点 ​

什么时候合、合哪些服,通常由活跃度数据说话,但技术侧的前置盘点同样决定成败:

  • 盘点双方的数据模型差异:两个服如果跑过不同版本,配置批次和字段未必一致。
  • 盘点特殊数据:跨服资产、限时活动进度、未到期的时间锁,逐类确认合并语义。
  • 盘点历史遗留:从未清理的废弃表和临时数据,会在迁移扫描时全部现形。
  • 选择低峰窗口:合服停机难以避免,窗口越冷清,等待的玩家越少。

盘点产出的是一份差异清单和一套逐项的决定,而不是一份通用的迁移脚本。脚本可以复用,决定不能复用,这是合服项目里常见的认知错位。

执行纪律:停写、干跑、核对 ​

迁移执行的技术难度不高,风险集中在操作纪律上。几条被反复验证过的纪律:

  • 先停写再迁移:确认所有写入已落盘、没有残留连接,再开始搬运。边写边迁是数据不一致的根源。
  • 干跑先行:迁移脚本支持演练模式,输出将要改写、合并、删除的清单,人工确认后再真跑。
  • 行数核对:每张表记录迁移前后的行数关系,任何不等都中止开服。
  • 回滚脚本与迁移脚本同等对待:迁移失败时能整体退回备份,且回滚路径真的演练过。
  • 窗口内留缓冲:迁移耗时按演练值放大预留,不把验证时间压到极限。
  • 高危步骤双人复核:批量改写和删除动作,由第二人确认参数后再执行。

这些纪律的共同点是增加前置成本、换取不可逆风险的下架。迁移出的问题大多是“当时想省一步”的利息。

冲突处理与补偿 ​

除了 ID,合服还要面对一批社交与经济层面的冲突,处理规则宜在合服前公示而不是事后解释:

  • 重名处理:玩家名、公会名加服号后缀,通常配合免费改名道具。
  • 排行榜:合并后重算,历史名次按归档保留。
  • 邮件与资产:各自保留、合并到名下,不互相覆盖。
  • 竞争性资源:领地、Boss 归属、限时活动进度,需要逐个定义合并规则,容易起争议的提前给补偿方案。

合服补偿不只是安抚手段,很多时候是对“玩家在一个服积累的身份和排名被稀释”的定价。补偿方案定得草率,运营侧要花数倍精力处理投诉;定得清楚,合服反而常被玩家当作新开始。

补偿发放本身也宜走幂等的资产管线:合服当天集中发放的补偿一旦被重复执行,代价远高于平时的普通发奖。

下线与归档 ​

下线是合服的另一种形态:不是合并两个活服,而是让一个服体面退场。下线的要点在于区分“计算下线”和“数据下线”:

  • 计算下线:进程回收、路由摘除、列表服隐藏,成本立刻释放。
  • 数据归档:玩家数据转入冷存储并保留查询途径,召回和审计时还能取回。

归档数据的保留期限和取回方式,通常需要在下线前定清楚。直接删除低活跃服数据是最容易引发不可挽回客诉的操作——低活跃不等于无玩家,一个服哪怕只剩几百人,那几百人的全部历史都在里面。

常见误区 ​

  • 把合服当成数据搬运,只规划了存储迁移,没规划 ID 重映射和引用改写。
  • 映射关系靠脚本运行时的临时变量传递,没有落成持久化的映射表。
  • 演练只验证“能跑完”,不核对行数和引用抽样,漏改留在暗处。
  • 回滚脚本从未真跑过,迁移失败时发现备份恢复流程同样生疏。
  • 冲突规则临场决定,玩家在论坛先于官方知道自己的名字被改成了什么。
  • 下线时连数据一起清掉,省下的存储远小于日后找回数据的代价。

迁移和合服的本质,是对系统数据模型的一次压力测试:平时靠运行时逻辑掩盖的身份模糊、引用松散、归属不清,都会在这类事件里集中现形。反过来讲,每次认真做一次合服,也都是把数据模型重新理清楚的机会。

实战案例:一次「合服三周后,邮件发给不存在的人」的撞号漏改排查 ​

案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「ID 撞号:最典型的隐性冲突」「引用完整性决定迁移质量」「执行纪律:停写、干跑、核对」用到深水区:合并当天一切正常,漏改在几周里陆续冒头——这是隐性冲突后置暴露的排查,也是「全量扫描为什么是唯一能兜住漏改的手段」的完整展开。

背景与现象:两服合并,源服 ID 整体加偏移 1000 万。合服当天行数核对全对;第 3 天起零星投诉陆续出现——好友列表多了陌生人、邮件退回「玩家不存在」、排行榜快照名字对不上号,三周累计 87 起,每一起都指向同一批旧 ID。

第一步:现象与口径——先确认漏改藏在哪类引用里 ​

冲突口径:显性冲突(重名、公会重名)已按后缀加改名处理完;出问题的是隐性撞号——旧 ID 在某处没有被偏移改写,仍在指向「另一个人」。

漏改口径:排查发现一张废弃的活动快照表仍在引用旧 ID——它是两次改版前的遗留副本,不在任何人的表清单记忆里;迁移脚本按记忆枚举了 42 张表,漏了这张。

核对口径:演练只验证了「能跑完」,没做行数与抽样比对——快照表不在核对清单里,漏改留在暗处。

第二步:分层归因——按本页漏改成因比对 ​

text
撞号漏改归因  占比
  存在无人知晓的数据副本,未全量扫描    57%
  演练不核对行数与引用抽样              26%
  映射表差点按临时数据清理              17%

逐项比对:漏改的本质原因几乎都是「存在一份没人知道的数据副本」——本页的原话,命中率最高;演练只验证能跑完——命中「漏改留在暗处」;映射表在合服后按「临时数据」进了清理候选——命中历史纠纷无从下手的那类亏。

第三步:根因——按记忆枚举表清单,全量扫描被跳过 ​

重映射是一次波及全部数据表的手术,靠「记得住所有表」来保证完整性注定失败:数据副本会随版本演进不断滋生,记忆枚举的覆盖率永远落后于现实。保证完整性靠的不是细心,而是机制——所有数据访问收拢到统一标识层,迁移以全量扫描改写。

第四步:处置与回填——用机制替换记忆 ​

  1. 全量扫描改写:迁移脚本改为扫 information_schema 全表,识别所有含旧 ID 的列,干跑输出改写清单。
  2. 核对放行门槛:行数关系加引用抽样比对,抽样命中旧 ID 即中止开服;高危批量改写双人复核。
  3. 映射表永久保留:写入资产清单,明确保留到产品生命周期结束,不参与业务读写但可回查。
  4. 干跑先行常态化:每次迁移先出「将要改写、合并、删除」清单,人工确认后再真跑。

复核账:投诉 87 起 → 下一次合服 0 起;干跑全量扫描比记忆枚举多识别出 3 张引用表;抽样比对在演练期拦下 1 处视图残留旧 ID。

回填清单:

回填项动作回填处
统一标识层数据访问收拢过标识层,迁移只改这一层与底层数据引用完整性决定迁移质量
全量扫描迁移脚本扫全表识别旧 ID 列,不按记忆枚举引用完整性决定迁移质量
核对放行行数关系加抽样比对,不等即中止开服执行纪律:停写、干跑、核对
映射表永久化映射表入资产清单,保留到产品生命周期结束映射表为什么值得永久保留

案例的三个教训 ​

  1. 隐性冲突不按时钟表爆发。合服当天全对,漏改以「邮件发给不存在的人」「好友列表里多了个陌生人」的形式在几周里陆续冒头。
  2. 记忆枚举必然漏,全量扫描才能兜住。没人知道的数据副本永远存在,覆盖率要靠机制不靠细心。
  3. 映射表是历史数据的解释权。按临时数据清理一次,几年后的纠纷就无从下手。

Used By ​

合服与迁移方案评审;迁移演练与执行纪律;冲突处理与补偿预案。

关系链:分区决定成本 → ID 撞号 → 引用完整性 → 映射表 → 时机盘点 → 执行纪律 → 冲突补偿。上游:capacity/02(有状态扩缩)。下游:database 树(建模与引用完整性)、production/03(回滚演练)。

Reference ​

本页为工程经验归纳;页内实战案例为教学示例(数字取整简化)。

游戏知识体系