Q28: 如何解决数据一致性问题?
核心结论
在线游戏里谈一致性,不能空泛地只讲分布式事务。更有用的做法是先问:
- 哪些数据必须强一致
- 哪些数据允许短时间延迟一致
- 哪些操作可以靠幂等和补偿解决
真正的工程方案通常不是“一套事务打天下”,而是按数据价值和业务后果分层处理。
一、先明确一致性问题发生在哪
常见来源包括:
- 并发更新同一份数据
- 多服务分别持有局部状态
- 缓存与数据库先后不一致
- 重试导致重复执行
- 跨服或异步链路出现部分成功
所以一致性问题不只是数据库事务问题,也是系统边界问题。
二、哪些场景必须更强一致
通常包括:
- 货币和资产
- 交易
- 拍卖成交
- 邮件附件领取
- 充值到账
这些链路一旦错,不只是数值抖动,而是直接影响用户资产和对账。
更常见的做法是:
- 单权威写入
- 幂等操作 ID
- 可靠落库
- 审计流水
三、哪些场景可以接受最终一致
例如:
- 排行榜展示
- 好友在线状态
- 世界公告分发
- 某些缓存视图
这类数据允许短时间延迟,只要最终收敛即可。
关键是不要把它们错误地塞进重事务链路。
四、解决一致性的几种常见手段
1. 单写者模型
很多游戏系统会让某类数据始终由唯一权威节点修改。
好处是:
- 并发冲突少
- 逻辑清晰
- 不需要大范围锁
这在玩家实体、场景实体、背包主对象里非常常见。
2. 数据库事务
本地事务适合单库内的强一致修改,例如:
- 扣钱加道具
- 邮件状态加附件领取标记
但前提是操作边界清晰,且不要把事务拉得过大。
3. 幂等
只要链路存在超时、重试、补偿,就必须有幂等能力。
典型做法:
- 业务唯一 ID
- 去重表
- 唯一约束
- 重复请求返回旧结果
4. 事件和补偿
跨服务链路里,很多时候比强行做分布式事务更现实的是:
- 明确主操作
- 记录事件
- 下游异步消费
- 出错时做补偿或重放
五、不要滥用分布式事务
在线游戏不是不能做分布式事务,而是很多链路并不适合。
原因包括:
- 调用链长
- 延迟敏感
- 节点波动常见
- 回滚语义复杂
在很多业务里,单权威加幂等加流水,比 2PC 更可维护。
六、缓存一致性怎么处理
缓存一致性是最常见的实际问题之一。
常见策略有:
- 先写库再删缓存
- 由权威节点负责更新缓存
- 对热点数据设置短 TTL
- 关键链路避免多处可写缓存
重点是减少多写入口,而不是幻想缓存永远和数据库完全同步。
七、工程上更稳妥的组合
很多系统最后会收敛到:
- 资产类操作:单权威写入加本地事务加流水
- 跨服务更新:事件驱动加幂等消费
- 展示型数据:允许最终一致
- 缓存:作为加速层,不作为唯一真实来源
这比“一切都强一致”更现实。
八、常见误区
1. 一致性问题就是数据库隔离级别问题
远远不止。很多错误来自重试、缓存、异步消费和多服务写入。
2. 只要用了事务就不会错
事务只能覆盖它所在的数据边界,跨服务、跨缓存、跨队列的问题还在。
3. 所有数据都应该追求强一致
代价通常太高,而且很多展示数据并不值得。
参考资料
- 各类在线游戏资产链路、幂等与补偿设计实践资料
