Sharding 分片模式
你的电商平台用户量突破了 5000 万,订单表已经超过 1 亿行。MySQL 单表查询开始出现明显退化:SELECT * FROM orders WHERE user_id = ? 从 5ms 飙升到 200ms,DBA 告诉你:单表超过 5000 万行后,索引维护成本急剧上升,性能会持续恶化。
升级硬件?硬盘已经是最快的 SSD,内存加到 256G 还是不够。读写分离?写操作还是集中在主库。分库分表,被提上了日程。
这就是 Sharding 分片模式要解决的问题:当单机无法承载全部数据时,如何将数据分散到多个节点,同时保持高效的访问能力。
分片策略
哈希分片
哈希分片根据某个字段(如 user_id)的哈希值来决定数据归属的节点。
优点:
- 数据分布均匀,避免热点
- 写入负载均匀分散
缺点:
- 不支持范围查询(如
WHERE create_time BETWEEN ...) - 扩展时需要数据迁移
范围分片
范围分片根据数据的数值范围(如时间、ID 区间)来决定归属节点。
优点:
- 支持范围查询
- 按时间分片时,历史数据与近期数据自然分离
缺点:
- 容易产生热点(如当前月份的数据量远大于历史月份)
- 需要预先规划好范围边界
分片键选择
分片键是决定数据分配的关键字段。选择不当会导致数据倾斜、访问效率下降。
如果分片键选择不当,可能会出现「热点分片」问题。比如按 region 分片,北方用户占了 80%,导致某个分片负载过高。解决方案包括:1)选择基数更大的分片键;2)引入虚拟分片(Virtual Sharding);3)使用一致性哈希让数据分布更均匀。
跨分片查询:Scatter-Gather 模式
当查询条件不包含分片键时,需要查询所有分片,然后聚合结果。这就是散聚模式在分片场景中的应用。
跨分片聚合
分片路由:分片映射表 vs 一致性哈希
分片映射表
维护一个「分片键 → 分片节点」的映射表,查询时先查表再路由。
一致性哈希
一致性哈希使用环形空间来分配数据,新增或删除节点时只需要迁移少量数据。
分片迁移:数据重平衡
当需要扩展分片数量时,必须进行数据迁移。这是一个高风险操作,需要精心设计。
迁移步骤
- 双写:同时向新旧分片写入数据
- 历史数据迁移:将老分片的数据同步到新分片
- 数据校验:对比新旧分片数据一致性
- 切换读:切读流量到新分片
- 停写旧分片:关闭双写,移除旧分片
迁移期间的服务保障
思考题
问题 1:分片后如何保证全局唯一 ID?
参考答案
分片后无法使用数据库自增 ID,需要使用分布式 ID 生成方案:1)UUID,简单但无序、存储空间大;2)Snowflake 算法,使用时间戳 + 机器 ID + 序列号,适合分片场景;3)数据库号段模式,批量获取 ID 段,减少数据库压力;4)滴滴 Taxi ID 算法,高位时间戳 + 低位机器 + 序列号。分片键本身也可以包含业务含义(如时间 + 随机数),简化 ID 生成。
问题 2:分片数量如何确定?
参考答案
分片数量取决于几个因素:1)预估数据量和增长率,比如 3 年内可能达到 10 亿条,每表 5000 万上限,需要至少 200 个分片;2)单机承载能力,考虑磁盘容量、内存、CPU;3)扩展预留,一般预留 50% 的扩展空间;4)运维复杂度,分片数越多,运维成本越高。建议初期少一些(4-16),按需扩展比过度分片更好。
问题 3:分片键选择后,如何处理非分片键的查询?
参考答案
有几种常用方案:1)建立索引表(Mapping Table),记录分片键到非分片键的映射;2)多写一份数据,使用不同分片键(如用户 ID 和商品 ID 都作为分片键);3)全文索引或搜索引擎,将数据同步到 ES 中支持复杂查询;4)扫描所有分片并聚合(只适合低频查询)。选择取决于查询频率、数据规模、一致性要求等因素。