分布式环境下大容量数据持久化存储方案(基于Elixir/Erlang)
Elixir/Erlang分布式对局持久化架构优化方案
问题场景梳理
在Elixir/Erlang的技术设计规范中,官方推荐以进程为最小单元拆分工作任务,通过小体积消息完成进程间通信。但实际业务中经常遇到跨进程共享、存储非极小体积数据的需求,以多人卡牌游戏的典型场景为例:
- 每局对局对应一个独立的游戏主进程,玩家发起创建对局请求时,系统会启动对应进程承载整局的游戏逻辑
- 需求为实现对局数据的磁盘持久化,最初的设计顾虑是:如果直接在游戏主进程内执行持久化,磁盘IO、数据序列化过程可能阻塞主进程对玩家消息的处理,导致游戏响应延迟
- 因此最初规划拆分独立的「存储进程」,职责是异步接收游戏主进程投递的对局数据完成落盘:存储进程响应cast请求,游戏主进程在对局动作发生时把数据投递给存储进程后,立刻返回继续处理业务,不需要等待存储完成
- 这个方案衍生的问题是:如果存储进程部署在不同节点上,游戏数据需要走网络传输,慢网环境下即使单局数据量不大(测试中单局数据约4KB),也可能拉长进程通信耗时,甚至干扰单个进程的心跳机制
- 但独立存储进程的架构有不可替代的可靠性优势:支持跨节点故障恢复——如果游戏主进程崩溃,动态拉起的新进程可以向存储进程查询对应Game ID的对局存档恢复状态;如果存储进程崩溃,游戏主进程可以把最新对局数据发送到其他进程/节点完成存储。只有游戏主进程和对应存储进程同时崩溃时才会丢失部分数据,这在分布式环境甚至所有运行环境中都属于可接受的容错边界。同时正常运行时,承载游戏业务的节点(支持多节点分布式部署)和承载存储能力的节点之间仅需要单向增量传输数据(故障恢复场景除外),对网络速率要求不高。
通用优化思路与落地最佳实践
1. 本地预写削峰,隔离网络影响
不要让游戏主进程直接跨节点给远端存储进程发消息,先在游戏主进程所在节点做一层极低延迟的本地预写入:
- 可以在主进程内做非阻塞的本地WAL(预写日志)追加写,也可以在同节点启动一个极轻量的本地存储协程,主进程先把对局增量数据写到本地WAL,这个操作延迟在微秒级,4KB数据的写入完全不会阻塞主进程的业务处理
- 本地WAL写完之后,再由专门的本地同步进程把数据批量打包、压缩后,异步同步到远端存储集群,网络传输、远端写入的耗时完全不会碰主进程的消息处理路径,从根源上避免网络波动拖慢主进程心跳的问题
- 同步时不要每次传全量对局数据,只传对局状态的增量diff,通常单步操作的增量只有几百字节,进一步降低网络传输开销。即使远端存储集群完全故障,只要本地节点不宕机,数据就不会丢失,恢复时优先读本地WAL再补远端数据即可。
2. 调整VM参数,降低消息拷贝开销
Erlang VM本身提供了参数可以规避大消息对进程调度的干扰:
- OTP 19及以上版本支持分布式消息的堆外分配,开启对应调度器参数后,跨节点发送的消息不会占用发送进程的归约预算,不会阻塞主进程的正常消息处理和心跳响应
- 跨节点传输数据前,用
:erlang.term_to_binary/2加compressed: 1参数做轻量压缩,4KB的对局数据压缩后通常不到1KB,进一步缩短网络传输时间 - 给存储进程的投递操作不要放在玩家请求的关键处理路径上,可以放到主进程的低优先级
handle_info队列里,等核心业务逻辑处理完、给玩家返回响应之后,再做消息投递,完全不影响玩家感知到的响应速度。
3. 存储层分片副本,平衡可靠性与性能
存储进程不要用单实例架构,做分片+多副本部署即可同时满足可靠性和性能要求:
- 按对局ID做一致性哈希分片,每个分片部署1主2从副本,游戏主进程启动时就缓存对应分片的主节点路由,不需要每次投递时临时做路由查找
- 存储进程本身只做顺序追加写,不要做随机写,落盘时直接追加写磁盘文件,写完后异步更新索引,单存储进程可以轻松承载每秒上万次4KB级数据写入,不会成为性能瓶颈
- 故障切换时优先选同可用区的存储副本,避免跨可用区的慢网传输,副本之间走异步链式复制,不需要主副本等所有从副本写完再确认,把同步延迟降到最低。
4. 按业务场景做架构权衡
对于单局数据仅4KB的轻量卡牌游戏场景,不需要一开始就上复杂的跨节点存储架构:
- 完全可以直接在游戏主进程内把持久化任务投递到本地专用IO线程池(Elixir中可以用
Task.Supervisor启动不受主进程调度影响的本地异步任务),不需要跨节点部署存储进程,架构复杂度极低,也完全不会阻塞主进程业务 - 如果有跨节点容灾需求,再在本地持久化的基础上开启后台异步远端同步,同步过程完全在后台运行,玩家侧完全感知不到延迟
- 在可接受的容错边界内不需要做强一致同步,只要在对局结束、玩家主动退出这类关键节点做同步确认即可,中间状态的同步完全异步,哪怕丢失1-2步操作的状态,对卡牌游戏的体验影响也几乎可以忽略。
不需要为了追求理论上的零数据丢失,牺牲核心游戏交互的响应延迟——分布式系统不存在100%的可靠性,只要故障概率和数据丢失范围在业务可接受范围内,就是合理的架构。
内容的提问来源于stack exchange,提问作者vincent-lg
相关产品推荐
相关产品推荐

