合服是老服的续命术:两个服务器的人气合并,活跃度翻倍。合服的工程核心是数据迁移——几十万角色跨库搬家,ID 的冲突、名字的撞车、行会与邮件的归属,每一样都要有个说法。
---合服迁移:ID 重映射与重名处理
function Merge.run(srcDb, dstDb)
local idMap = {}
for _, role in ipairs(srcDb.query("SELECT * FROM roles")) do
local newId = Merge.nextId()
idMap[role.id] = newId
-- 重名检测:加服名后缀
if Merge.nameExists(dstDb, role.name) then
role.name = role.name .. "@" .. Merge.srcTag()
sendmail(role.name, 8009, "合服改名通知",
"您的名字与另一位玩家重复,已自动调整", "")
end
dstDb.exec("INSERT INTO roles SET ?", role)
end
Merge.saveIdMap(idMap)
end
迁移的第一件事是 ID 的重映射:源服与目标服的角色 ID 空间必然重叠,源 ID 到新 ID 的映射表是后续一切数据(背包、邮件、行会成员)改写罗盘。重名的处理加服名后缀并邮件告知——两个「血饮狂刀」在同一个服里必须有各自的姓名,自动改名加通知是折中的公平。迁移的事务按角色为单位:一个角色的全部数据(角色行、背包、变量)要么全迁要么全不迁,中断重跑不产生半人。
---背包与邮件的 ID 改写
function Merge.remap(idMap)
-- 背包的归属改写
for oldId, newId in pairs(idMap) do
dstDb.exec("UPDATE bags SET uid = ? WHERE uid = ?",
newId, oldId)
end
-- 邮件的收发双方改写
dstDb.exec("UPDATE mails SET toId = ? WHERE toId = ?",
function(row) return idMap[row.toId] or row.toId end)
-- 行会成员的归属改写
Merge.remapGuildMembers(idMap)
end
角色本体迁完后按映射表改写关联表:背包的归属、邮件的收发双方、行会的成员名单,改写的顺序按依赖图走(先角色后关联)。行会的重名同样处理后缀,两个「傲视天下」合并后一山容二虎靠名字的区分。金币与装备的合服通胀是运营的对价:合服奖励的发放与商城的回收活动同期上线,两份家底合并的通胀由活动消化。合服的公告提前两周:名字的变更规则、数据的合并范围、开服的时刻,知情权是减客诉的第一道闸。
迁移的对账三件套:角色数、物品数、邮件数的源目对比,差额逐条追;合服后的登录率与留存监控,迁移的质量由玩家的回归投票。
ID 映射曾经漏了邮件的发件方,系统邮件的归属错乱,映射的覆盖面以表清单驱动。重名的后缀曾经超了名字长度上限,超长名字的改写失败回滚整批,长度约束前置校验。行会的圣物格在两个仓库合并时超容,超容的部分转邮件附件,合服的容量预案写进流程。
合服的演练在测试环境全量走两遍:生产数据的脱敏副本跑真实迁移脚本。合服的窗口放凌晨低峰,迁移的耗时有上限预估,超时的熔断回滚。合服后的数据修复通道开满两周,玩家反馈的资产问题优先处理。
本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 沙巴克占领行会坐收税收却只进不出,非占领行会的玩家心里不平衡。税务官玩法把税收做成明账:占领行会对摆摊与…
实战应用:用在哪里 高端玩家婚姻系统只有仪式没有过程,社交绑定缺一个高光事件。比武招亲做成擂台玩法:擂主设擂摆彩头,攻擂者报…
实战应用:用在哪里 开服七日限时礼包发到第六天断了粮,9 月 1 日当天领奖按钮灰的,投诉当天破千。事故根因是一条数字日期循…
实战应用:用在哪里 玩家拔线不退游戏,连接半开着,人不在指令也没有,在线数虚高,容量预案被假数据带着跑。心跳探测给每个在线玩…
实战应用:用在哪里 周三发了合成系统补丁,周四又改强化概率,两条变更叠加后玩家分不清是哪个改出来的。发布日历把全月变更集中排…
实战应用:用在哪里 容量预案需要知道滚动一小时的副本进入峰值,而不是全天累计。滑动窗口用 60 个槽位每槽记一分钟的进入数,…