完整课程入口:996 全套课程体系 | Lua 学习路径 | 幂尔框架 mirs.cn
玩家数据几十个字段,整包存档每次全量序列化,既慢又放大写盘压力。脏字段(dirty field)增量存档只写"发生变化的部分":内存里维护字段级脏标记,落盘时只序列化脏字段,写入后清标记。适用于:玩家属性定时保存、活动进度持久化、跨服快照同步——凡是高频保存大数据的场景都适用。
local function track(data, dirty)
return setmetatable({}, {
__index = function(_, k) return rawget(data, k) end,
__newindex = function(_, k, v)
local old = rawget(data, k)
if old ~= v then
rawset(data, k, v)
dirty[k] = true
end
end,
})
end
local raw = { level = 30, gold = 8800, kills = 12 }
local dirty = {}
local player = track(raw, dirty)
player.kills = 13 -- 只有 kills 被标记
落盘时遍历 dirty 表,只序列化标记字段,写完清空 dirty。实测某玩家数据 38 个字段单次只变化 2~3 个,存档体积从全量 6KB 降到增量 0.4KB,写盘次数不变但单次体积缩小 90% 以上。
定时全量兜底。 增量基于"上次快照"的假设,进程异常退出可能丢失标记——每 10 分钟做一次全量保存作为基线,增量只覆盖两次全量之间的变化。字段校验白名单。 可写白名单外的字段名直接 error,防止拼写错误的字段静默失败(写进了代理表但真表没有)。加载即比对。 读取增量合并到全量后,用字段计数校验完整性,条目数对不上触发全量重拉。
脏标记与弱表缓存(计算缓存篇)、对象池(战斗循环篇)互不冲突:缓存解决"重复计算",池解决"重复创建",增量存档解决"重复写入"。三件套齐备后,数据层的三类重复开销就都收敛了。上线节奏建议:先在活动进度这类低风险数据上验证一周,再推广到玩家核心属性——存档方案的切换永远要小步走。
推荐节奏:增量落盘每 60 秒一次(只写脏字段),全量基线每 10 分钟一次,M2 重启钩子触发一次全量。三档配置实测某服数据回档窗口从 10 分钟缩到 60 秒以内,写盘总量反而下降四成。配置参数集中在存档模块头部,改动时对照在线峰值调整,避免高峰期全量保存挤占 I/O。