Skip to content
On this page

分库分表策略详解

一、什么是分库分表?

分库分表是数据库水平扩展的核心技术,通过将数据分散到多个数据库或数据表中,解决单库单表数据量过大、性能瓶颈的问题。

基本概念

概念说明示例
分库将数据分散到多个数据库中用户表按用户ID取模,分散到 user_db_0user_db_1user_db_2
分表将数据分散到同一数据库的多个表中订单表按月份拆分:order_202401order_202402order_202403
分库分表同时进行分库和分表先分库,每个库内再分表

二、为什么需要分库分表?

1. 单库单表的瓶颈

性能瓶颈

  • 数据量过大:单表数据超过千万级别,查询性能急剧下降
  • 索引效率降低:B+树索引深度增加,查询需要更多磁盘IO
  • 锁竞争加剧:高并发下,表级锁或行级锁竞争激烈
  • 连接数限制:单库连接数有限,无法支撑高并发

容量瓶颈

  • 存储空间:单库存储容量有限
  • 备份恢复:数据量过大,备份和恢复时间过长
  • 运维困难:DDL操作(如添加索引)耗时过长,影响业务

2. 何时考虑分库分表?

判断标准

  • ✅ 单表数据量超过 500万-1000万
  • ✅ 数据库QPS超过 5000-10000
  • ✅ 单表文件大小超过 10GB
  • ✅ 查询响应时间明显变慢(>1秒)
  • ✅ 频繁出现慢查询

注意:不要过早优化!优先考虑:

  1. 优化SQL和索引
  2. 读写分离
  3. 缓存优化
  4. 数据库参数调优

三、分库分表策略

1. 水平分表(Horizontal Sharding)

将同一张表的数据按拆分到多个表中,表结构相同。

策略分类

① 范围分片(Range Sharding)

原理:按数据范围划分

示例

sql
-- 按用户ID范围分表
user_0: 0 <= user_id < 1000000
user_1: 1000000 <= user_id < 2000000
user_2: 2000000 <= user_id < 3000000

优点

  • 实现简单,易于理解
  • 范围查询效率高
  • 数据迁移方便

缺点

  • 容易产生数据倾斜(热点数据集中)
  • 需要提前规划范围
  • 扩容需要迁移数据
② 哈希分片(Hash Sharding)

原理:对分片键进行哈希运算,取模分配到不同表

示例

python
# 按用户ID取模分表
shard_num = user_id % 4
table_name = f"user_{shard_num}"

# 用户ID=1001 -> 1001 % 4 = 1 -> user_1
# 用户ID=1002 -> 1002 % 4 = 2 -> user_2

优点

  • 数据分布均匀,避免热点
  • 实现简单,查询路由明确
  • 适合高并发场景

缺点

  • 扩容困难(需要重新哈希)
  • 范围查询需要跨表
  • 无法按业务逻辑分片
③ 一致性哈希(Consistent Hashing)

原理:将哈希值空间组织成虚拟环,节点和数据都映射到环上

示例

哈希环: 0 ----1000----2000----3000----4000----0
节点:   Node1  Node2  Node3  Node4
数据:   key1->Node2, key2->Node3, key3->Node4

优点

  • 扩容时只需迁移少量数据
  • 节点增减影响范围小
  • 适合动态扩容场景

缺点

  • 实现复杂
  • 可能出现数据倾斜(虚拟节点解决)
④ 目录分片(Directory Sharding)

原理:维护一个分片映射表,记录数据到分片的映射关系

示例

sql
-- 分片映射表
CREATE TABLE shard_mapping (
    user_id BIGINT PRIMARY KEY,
    shard_id INT,
    table_name VARCHAR(50)
);

-- 查询时先查映射表
SELECT shard_id, table_name FROM shard_mapping WHERE user_id = 1001;

优点

  • 灵活,支持复杂分片规则
  • 易于调整分片策略
  • 支持非均匀分片

缺点

  • 需要额外维护映射表
  • 映射表可能成为瓶颈
  • 查询需要两次访问

2. 垂直分表(Vertical Sharding)

将同一张表按拆分,将不常用或大字段分离到扩展表。

示例

sql
-- 主表:存储常用字段
CREATE TABLE user (
    id BIGINT PRIMARY KEY,
    username VARCHAR(50),
    email VARCHAR(100),
    created_at TIMESTAMP
);

-- 扩展表:存储不常用字段
CREATE TABLE user_ext (
    user_id BIGINT PRIMARY KEY,
    bio TEXT,
    avatar_url VARCHAR(255),
    settings JSON
);

优点

  • 减少单表字段数,提升查询效率
  • 大字段分离,减少IO
  • 可按访问频率优化

缺点

  • 需要JOIN查询
  • 事务处理复杂
  • 数据一致性需要保证

3. 垂直分库

按业务模块将不同表拆分到不同数据库。

示例

user_db:    用户相关表(user, user_profile, user_settings)
order_db:   订单相关表(order, order_item, payment)
product_db: 商品相关表(product, category, inventory)

优点

  • 业务解耦,便于独立扩展
  • 降低单库压力
  • 便于团队分工

缺点

  • 跨库事务复杂(需要分布式事务)
  • 跨库JOIN困难
  • 数据一致性保证困难

四、分片键(Sharding Key)选择

1. 分片键的重要性

分片键是决定数据分布的关键字段,选择不当会导致:

  • 数据倾斜(热点问题)
  • 跨分片查询增多
  • 扩容困难

2. 选择原则

① 高基数(Cardinality)

分片键的值应该尽可能分散,避免集中在少数几个值。

好的选择

  • 用户ID(每个用户唯一)
  • 订单ID(每个订单唯一)
  • UUID(全局唯一)

不好的选择

  • 性别(只有2-3个值)
  • 状态(只有几个状态值)
  • 地区(值有限)

② 业务相关性

分片键应该与业务查询模式匹配,减少跨分片查询。

示例

sql
-- 如果经常按用户ID查询订单
-- 分片键选择:user_id(而不是order_id)
-- 这样同一用户的订单在同一分片
SELECT * FROM order WHERE user_id = 1001;

③ 避免热点

避免选择会导致数据集中在少数分片的字段。

问题示例

sql
-- 按创建时间分片,新数据都集中在最新分片
order_202412: 大量新订单(热点)
order_202411: 少量订单
order_202410: 更少订单

解决方案

  • 使用组合分片键:(user_id, create_time)
  • 使用哈希分片而非范围分片

3. 常见分片键选择

业务场景推荐分片键原因
用户表user_id高基数,查询模式匹配
订单表user_id按用户查询多,避免跨分片
订单表order_id如果按订单ID查询多
商品表category_id + product_id组合键,按分类查询
日志表user_id + date组合键,按用户和时间查询

技术文档集合