单机小游戏也能做多区?聊聊我的多区存档隔离方案

修改于04/2687 浏览开发心得
大家好,我是《深渊炼狱:堕落之塔》的开发者。游戏上线后随着玩家增多,老区生态固化、新玩家难以融入的问题逐渐显现。于是我做了一套多区存档系统——不依赖多套服务器,纯靠云变量的 key 前缀实现数据隔离,目前已经稳定运营到 8 个区服。
这篇帖子分享一下我的实现思路,希望对同样在做轻量级多区的开发者有参考价值。
horizontal linehorizontal line

一、核心思路:Key 前缀隔离

整个方案的核心就一句话:同一个玩家的不同区数据,靠 key 前缀区分
1区:progress_chapter = 15        (无前缀,兼容老数据)
2区:z2_progress_chapter = 3
5区:z5_progress_chapter = 8
实现上就是一个 zKey() 函数,所有存取操作都经过它:
```lua
local zonePrefix_ = ""  -- 1区为空,2区为"z2_",以此类推
local function zKey(baseKey)
    if zonePrefix_ == "" then return baseKey end
    return zonePrefix_ .. baseKey
end
```
为什么 1 区不加前缀? 因为游戏上线初期只有一个区,已经有大量玩家数据。加前缀意味着所有老玩家的存档都要迁移,风险太大。所以 1 区保持原始 key 不动,2 区以后才加前缀。这样老数据零迁移、零风险。
horizontal linehorizontal line

二、分区独立版本控制(精准清档)

运营中难免遇到需要清档的情况(比如数值崩了、刷了 BUG)。全服清档影响太大,所以我做了分区独立版本号
lua
Config.SAVE_DATA_VERSION = 2      -- 1区版本(改这个只清1区)
Config.Z2_SAVE_DATA_VERSION = 1   -- 2区版本(改这个只清2区)
Config.Z3_SAVE_DATA_VERSION = 1   -- 3区版本
-- ...以此类推到8区
玩家登录某区时,检查云端存的版本号和代码里的版本号是否一致。不一致就触发该区清档,其他区完全不受影响。
这个能力在运营中非常实用——比如 5 区出了刷金 BUG,只需要把 Z5_SAVE_DATA_VERSION 加 1,发个版本就只清 5 区,其他 7 个区的玩家毫无感知。
horizontal linehorizontal line

三、切区保护:防止数据串区

多区最容易出的 BUG 就是数据串区——玩家从 4 区切到 5 区,前缀已经改成 z5_ 了,但 4 区还有个异步回调没执行完(比如广告奖励、邮件领取),这时候回调触发保存,数据就写到 5 区去了。
我的解决方案是加了一个切区过渡锁
```lua
local zoneTransitioning_ = false
function CloudSave.BeginZoneTransition()
    zoneTransitioning_ = true  -- 切区开始,锁住所有Save
end
function CloudSave.EndZoneTransition()
    zoneTransitioning_ = false -- 新区数据加载完毕,解锁
end
```
切区期间所有 Save 操作都会被拦截并打日志,直到新区的数据完全加载完毕才解锁。这样即使有异步回调延迟触发,也不会污染新区数据。
另外还有一个存档保护锁:如果云端加载失败(网络超时等),会锁住保存功能,防止用空数据覆盖玩家的真实存档。宁可这局不存,也不能把人存档清了。
horizontal linehorizontal line

四、排行榜也要隔离

排行榜的 key 同样走前缀机制,每个区有独立的战力榜、关卡榜、无尽模式榜:
1区:best_score       →  1区关卡排行榜
5区:z5_best_score    →  5区关卡排行榜
但有些榜是跨服的(比如每日广告次数排行),这类榜不加区服前缀,所有区的玩家在同一个榜上竞争。
排行榜还支持版本号重置,GM 清榜时递增版本号,key 变成 best_score_v2,旧榜数据自然失效,不需要逐条删除。
horizontal linehorizontal line

五、GM 广播:一次写入,全区可见

GM 发公告或者调整活动配置时,需要所有区的玩家都能看到。我的方案是利用排行榜的附带数据机制——GM 把公告数据写入每个区的排行榜:
lua
local ALL_ZONE_PREFIXES = {"", "z2_", "z3_", "z4_", "z5_", "z6_", "z7_", "z8_"}
-- 遍历所有区,逐一写入广播数据
玩家进入任意区服时,读取该区排行榜就能拿到最新公告。这样不需要额外的广播通道,复用已有的云变量基础设施就够了。
horizontal linehorizontal line

六、区服状态动态管理

选区界面支持多种状态展示,基于时间自动切换:
| 状态 | 含义 | 颜色 |
|------|------|------|
| 推荐 | 当前活跃区,鼓励进入 | 绿色 |
| 已满 | 老区,人多生态稳定 | 橙色 |
| 即将开放 | 还未到开服时间 | 灰色 |
| 停止注册 | 不再允许创建新角色 | 橙色 |
新区还有自动福利激励(比如 2 区以上自动发月卡),引导玩家往新区分流。
horizontal linehorizontal line

七、三层存档容灾

存档不是只有云端一条路,我做了三层 fallback:
clientCloud(云变量直连)
    ↓ 不可用
Server Proxy(服务端代理转发 serverCloud)
    ↓ 不可用
本地文件 save.json(最终兜底)
本地存档文件名也跟着区服走:1 区是 save.json
,2 区是 z2_save.json
。即使在完全离线的情况下,各区数据也不会混。
horizontal linehorizontal line

总结

整套方案的核心就是一个前缀函数 + 两把保护锁,没有什么高深的架构,但胜在简单可靠:
  • 前缀隔离:零迁移成本,新增区服只需加配置
  • 独立版本号:精准清档,互不影响
  • 切区保护锁:杜绝异步回调导致的数据串区
  • 存档保护锁:加载失败时禁止写入,保护玩家数据
目前 8 个区服稳定运营,没有出过数据串区的事故。如果你的游戏也有类似的多区需求,但又不想搞复杂的多服部署,这个方案可以参考。
有问题欢迎在评论区交流~
7
6
4