Q27: 如何设计数据库表结构?
核心结论
MMO 的表结构设计,核心不是把所有对象都铺平成一堆表,而是先明确:
- 哪些数据是权威主档
- 哪些数据是高频变更
- 哪些数据适合拆表或事件化
- 哪些查询是真正需要被支持的
好的表结构是业务访问模式驱动的,不是按“系统模块名”机械拆表。
一、先按数据性质拆,而不是按代码类名拆
常见可分成几层:
1. 账号层
例如:
- 账号
- 登录信息
- 封禁状态
- 设备与风控信息
2. 角色主档层
例如:
- 角色基础信息
- 职业、等级、经验
- 所属区服
- 当前基础成长状态
3. 子系统层
例如:
- 背包
- 任务
- 邮件
- 好友
- 公会
- 拍卖行
这些模块变化频率、容量上限、查询模式都不一样,通常不适合硬塞进一张大表。
4. 流水与日志层
例如:
- 货币变更流水
- 道具变更流水
- 交易记录
- 发奖记录
这类数据的职责是审计和追溯,不该和主档表混为一体。
二、角色主档表应该尽量稳定
角色主档表适合放:
- 主键和外键关系
- 少量高价值核心字段
- 需要快速读取的角色基础信息
不适合无限塞入:
- 长列表
- 大对象 JSON
- 高频变化细粒度子状态
否则主档表会变成超级宽表,后续扩展和写入都会很痛苦。
三、什么时候拆表
出现下面情况时,通常应考虑拆表:
- 字段数量持续膨胀
- 查询模式完全不同
- 更新频率差异极大
- 数据生命周期不同
- 存储规模增长明显
例如:
- 玩家基础信息和背包数据不应放同表
- 邮件正文和邮件索引也常常分离
- 排行榜快照和实时角色主档不适合共表
四、关系建模要服务于真实查询
设计表结构时必须先问一句:
“这张表以后会被怎么查?”
例如好友系统,真正常见的查询是:
- 查某玩家好友列表
- 查双方是否已是好友
- 查在线好友
那索引和关系表就应围绕这些查询设计,而不是单纯追求范式优雅。
五、不要过度依赖单个大 JSON 字段
把复杂模块全塞进 JSON 确实省开发时间,但长期代价很高:
- 查询困难
- 局部更新不方便
- 索引能力差
- 数据校验和迁移麻烦
JSON 适合:
- 扩展字段
- 低频访问配置
- 不需要复杂查询的小型结构
但不适合成为核心主档的主要载体。
六、索引设计比“字段全不全”更重要
在线游戏很多问题不是字段少,而是索引方向错了。
常见原则:
- 主键简单稳定
- 高频查询必须有直接索引
- 避免无意义的过多索引
- 组合索引要贴近真实 where 条件
索引太少会慢,索引太多又会放大写入成本。
七、高价值系统要有流水表
货币、道具、拍卖、邮件领奖这类系统,不能只保留“当前状态”。
更稳妥的做法通常是:
- 主档表保存当前结果
- 流水表保存变化过程
- 幂等 ID 贯穿关键操作
这样才能支持:
- 对账
- 回滚补偿
- 作弊追查
八、分库分表不要过早,但也不要假装永远用不上
不是所有项目一开始都要上分片,但表结构应该至少避免明显阻塞未来拆分。
例如:
- 主键策略要可扩展
- 尽量减少跨模块强事务绑定
- 预留按区服、账号、角色维度拆分的可能
九、常见误区
1. 表越少越简单
短期可能简单,长期通常会变成耦合灾难。
2. 先全部范式化,性能问题后面再说
在线游戏很多表天然偏读写混合,完全照教科书范式设计未必合适。
3. 所有子系统都用一套通用“大字段扩展”
这通常会牺牲查询能力和演进能力。
参考资料
- 各类在线游戏角色主档、流水表与分库分表实践资料
