我正在使用一些相当深奥的元素(理论上很好但在实践中很少使用)重写 MMORPG 服务器引擎,并且有点怀疑。其中的一些元素是“可靠的”——但做“又一个 MMO 服务器”的目的是在生产级代码中测试其中的一些概念。
然而,我希望这里的某个人可能对 PostgreSQL 分区模型有一些实践经验,并且能够“借”一些专业知识给这个项目。
概述
下面有一个 TL;DR 版本。
- 核心操作系统结构是一个 Linux 实例集群,可能位于云系统上。用于监控整体系统性能和使用外部 API 启动新实例的存根计划用于以后的实现;出于测试目的,我们正在考虑使用亚马逊,但我们正在敞开大门,使其成为 RackSpace 和其他提供商的可插拔模块,甚至通过重新配置物理池、“热备用”来做一些顽皮的事情专用机架上的服务器。
- 主要的 MMO 逻辑基于“纯”实体-组件-系统模型。实体“是”长整数 ID;组件是关系数据记录或记录集(列表)(JOINable 引用);系统就是功能。系统提供有关它们需要访问哪些组件的元数据,并且方法?整个集群的数据位置基于预测这些访问的规划器,试图在同一主机上继续运行相同的系统。也就是说:访问同一实体的同一组件的系统往往位于同一台机器上。
- 关系数据存储由 PostgreSQL 数据库支持。之所以选择它,是因为它是自由软件,并且在某些方面比 MySQL 提供“更好”(出于我们的目的)SQL/ACID 服务。让我们假设这是一个不可变的(已经有很多争论了)。
- 服务器主机实例代表一个单一的游戏世界,占据一个连续的坐标空间:游戏世界本身没有不连续性(例如关卡;星系统;世界实例)。因此,实体可以根据动态区域划分到主机上,其“大小”可能在运行时发生变化。这些区域可以或多或少地在 SQL 中有效地分区是“给定的”:例如,我们可以定义一个“Y=-10 处的分区平面”,并根据它们的 Y 坐标或类似方式拆分实体。用于此的精确机制可能会在模拟负载下进行一些实验。更改此分区规则将是一个相对不常见的事件:也许 5 或 10 分钟的计时器可能会监视服务器负载并尝试确定更优化的拆分。
- 服务器主机可以是数据库服务器(集群)、游戏逻辑服务器或(集群大小=1 系统)两者。我们可以在主规划器系统的控制下启动数据库实例,并以任意复杂的方式配置它们以管理加入集群等,这是一个“给定的”。这可能包括创建底层 RAMdisc 或分区规则等。
- 因此,给定主机的数据集可能是某组表(组件)的某种组合,但只对给定的实体池特别感兴趣,这些实体是通过单独的标准选择的。出于效率的目的,我们可能会在
entities
表中存储一列,指示它属于哪个“服务器位置池”,或者是提供该映射的单独表,或具有该效果的某些内容。
- 有一个固有的假设,即只要后端数据库日志不被破坏,单个记录包含足够的数据来保持完整性。IE:只要是一个交易
COMMIT
,我们就不会在崩溃时非常关心我们是否获得任何特定交易的“之前”或“之后”图像。我想我在口头上混淆了这个概念:换句话说,为了崩溃恢复的目的,我们并不十分担心整个交易是否丢失,只要我们丢失整个交易。丢失集群主机(例如机器着火)的可能性在规划器级别处理,如果我们检测到心跳丢失,则可以相当快地重新分配该主机的责任区域。
- 几乎每个主机的写入量都是读取量的一半左右,这比许多数据库系统的设计预期要高得多。
概述 (TL;DR)
我们有一堆带有 PostgreSQL 的 Linux boxen。我们有一些函数可以接收数据的子集,这些数据可以使用SELECT
或VIEW
在这些主机上运行来定义,并且写出几乎与读取一样多的更改。
纯理论模型
这是它变得脆弱的地方:
组件直接映射到关系表和行。例如,假设最终有一个 Position 组件。实体 ID 将是表上的主键,对于一致性检查,也是entities
仅包含 PKSERIAL8
字段的表的外键。位置表然后有,比方说,x NUMERIC, y NUMERIC, z NUMERIC …