Q35: 如何实现数据库分片?
核心结论
分片不是为了“显得架构高级”,而是当单库在容量、吞吐、维护窗口或故障域上已经撑不住时,才需要把数据拆开。
真正重要的不是分成多少片,而是先回答:
- 为什么要分
- 按什么键分
- 以后怎么扩容
- 跨片查询和跨片事务怎么收敛
如果这些问题没想清楚,分片只会把问题从一个库复制成很多库。
一、先判断是不是真的需要分片
很多系统在真正分片前,往往还有更便宜的手段:
- 索引优化
- 读写分离
- 冷热分层
- 归档历史数据
- 缓存热点数据
如果这些都没做,过早分片通常只会增加复杂度。
二、最常见的两类拆分
1. 水平拆分
把同类数据按行分到不同库或表里。
常见于:
- 玩家主档
- 背包
- 邮件
- 日志流水
2. 垂直拆分
把不同业务模块拆到不同库。
例如:
- 账号库
- 角色主档库
- 社交库
- 拍卖库
这更像按业务边界拆,而不是按数据量拆。
三、分片键怎么选最重要
分片键决定了:
- 数据分布是否均匀
- 查询是否容易路由
- 扩容是否容易
在线游戏里常见分片键包括:
player_idaccount_idserver_id- 时间维度
但没有万能键,关键看业务访问模式。
例如日志很适合按时间拆,角色主档更适合按角色或账号拆。
四、分片最怕什么
最怕的是查询和事务模型与分片键不匹配。
常见代价包括:
- 跨片聚合变重
- 跨片事务复杂
- 扩容迁移困难
- 热点集中到少数分片
所以分片键不能只看“好算”,还要看“以后怎么查、怎么迁、怎么扩”。
五、扩容要在第一天就想好
很多系统第一次分片能跑,但第二次扩容就开始痛。
需要提前考虑:
- 分片数是否可扩
- 新旧分片如何迁移
- 路由规则如何升级
- 历史数据是否回迁
一致性哈希、逻辑分片号、路由层抽象,都是为后续扩容服务的。
六、跨片事务不要轻易美化
一旦跨片写入变多,系统复杂度会明显上升。
更务实的做法通常是:
- 让同一业务对象尽量落在同一片
- 关键链路减少跨片事务
- 必要时用幂等和补偿
而不是一开始就把所有跨片一致性都压给分布式事务。
七、在线游戏里更现实的分片思路
很多时候会采用组合方式:
- 玩家主数据按
player_id拆 - 日志与流水按时间拆
- 跨服数据按
server_id或大区拆 - 高价值模块单独成库
这比强行一套规则覆盖所有表更实际。
八、常见误区
1. 分片就是取模
取模只是最简单路由方式,不等于完整分片方案。
2. 先分 64 片以后就省心
不对。过多空片和不合理路由同样会带来运维成本。
3. 分片后性能问题自然消失
热点、不合理查询、跨片聚合仍然可能把系统拖垮。
参考资料
- 各类在线游戏分库分表与按时间归档实践资料
