目录
2.1 垂直分库(Vertical Database Partitioning)
2.2 垂直分表(Vertical Table Partitioning)
在高并发、高可用的业务场景中,分库分表(Sharding)是一个常见的数据库架构优化策略。以下是一个高可用分库分表方案,涵盖了数据拆分、路由策略、高可用架构及容灾恢复等方面。
一 水平分库分表
水平分库分表(Sharding) 是将 同一张逻辑表的数据 按一定规则拆分到 多个数据库 或 多个表 中,解决 单库存储瓶颈 和 单表查询性能下降 的问题。
下面详细讲解 水平分库分表的策略、实现方式、常见问题及优化方案。
1. 水平分库(Database Sharding)
水平分库 是指将 数据按某个规则拆分到不同的数据库实例,每个数据库都包含相同的表结构。
1.1 水平分库策略
(1) 哈希分库(取模分库)
规则:通过某个字段(如 user_id
)对数据库数量 N
取模,将数据均匀分配到 N
个数据库。
公式:
db_index = user_id % N
示例(假设 N=4 个数据库):
user_id = 101 -> db_1
user_id = 202 -> db_2
user_id = 303 -> db_3
user_id = 404 -> db_0
✅ 优点:
- 数据分布均匀,查询性能稳定
- 适用于 数据无明显时间特征 的业务(如用户、订单)
❌ 缺点:
- 扩容难(
N
变了需要重新分配所有数据)
优化方案:使用 一致性哈希(Consistent Hashing)解决扩容问题。
(2) 范围分库
规则:根据某个字段的范围(如 order_time
或 user_id
)拆分到不同的数据库。
示例(按时间拆分):
- db_2023 → 2023年数据
- db_2024 → 2024年数据
- db_2025 → 2025年数据
✅ 优点:
- 方便归档,历史数据可拆分到归档库
- 扩容简单,新增库时直接划定新范围
❌ 缺点:
- 热点问题:新数据库负载更高,查询可能集中在某个库上
- 跨库查询复杂,需要路由逻辑
优化方案:结合 冷热数据分离,将历史库查询优化成离线任务。
(3) 一致性哈希分库
规则:使用一致性哈希算法,让数据分布更平滑,并支持动态扩容。
- 普通哈希:
user_id % 4
- 一致性哈希:使用哈希环 (
hash(user_id) -> 虚拟节点
)
✅ 优点:
- 支持扩容,新增库时仅调整部分数据
- 负载均衡更优
❌ 缺点:
- 实现复杂,需要 自定义路由算法 或使用 中间件(ShardingSphere, MyCAT)
1.2 数据路由
由于数据分布在不同数据库中,查询时需要根据分库规则路由到正确的数据库。
(1) 应用层路由
在代码里自行管理分库逻辑,例如:
def get_db_index(user_id, db_count=4):
return user_id % db_count # 计算落在哪个数据库
适用于小型项目,但扩展性不强。
(2) 代理层路由
使用 数据库代理(如 ShardingSphere, MyCAT)自动管理数据分片,应用层只需写 SQL,不需要管理分片逻辑。
2. 水平分表(Table Sharding)
水平分表 是指 将同一张表的数据拆分到多个表中,但仍存储在同一个数据库内,用于解决单表数据量过大的问题。
2.1 水平分表策略
(1) 哈希取模分表
规则:按照某个字段(如 user_id
)对表数量 M
取模,分配数据。
公式:
table_index = user_id % M
示例(假设 M=4
个分表)
user_id = 101
→user_1
user_id = 202
→user_2
user_id = 303
→user_3
user_id = 404
→user_0
✅ 优点:
- 数据分布均匀,查询负载平均
- 适用于无时间特征的业务
❌ 缺点:
- 扩展时需要重新分配数据(和哈希分库问题相同)
优化方案:一致性哈希 or 预留空表(如 user_00
~ user_99
)。
(2) 范围分表
规则:按 user_id
或 时间
拆分表
示例(按 order_time
拆分):
orders_202301
orders_202302
orders_202303
✅ 优点:
- 可控,按业务增长扩展表
- 适用于有时间特征的业务(如日志、订单)
❌ 缺点:
- 热点问题(最新表访问最多)
- 跨表查询复杂
优化方案:使用 中间件 或 视图合并。
2.2 水平分表查询
查询数据时,需要知道数据在哪个分表:
def get_table_index(user_id, table_count=4):
return user_id % table_count # 计算目标表
或者使用数据库中间件(如 ShardingSphere)自动分表路由。
3. 水平分库 vs 水平分表
水平分库 |
水平分表 |
|
拆分级别 |
库级别 |
表级别 |
适用场景 |
大规模数据,单库压力大 |
单表数据量大,查询变慢 |
扩展性 |
需要 多数据库,更复杂 |
只拆表,不增加数据库 |
事务支持 |
需要 分布式事务 |
单库事务仍可用 |
查询优化 |
需要 跨库查询优化 |
只需 跨表查询优化 |
👉 结论:
- 如果数据库负载过高 → 先考虑水平分库
- 如果单表查询变慢 → 先考虑水平分表
- 如果单库+单表都受限 → 水平分库+水平分表
4. 结合水平分库+分表
实际业务中,我们通常会结合使用:
- 先按业务分库(垂直拆分:
user_db, order_db, payment_db
) - 再水平分库(按
user_id
取模拆分多个数据库) - 最后水平分表(订单表
orders_2023, orders_2024
)
最终架构:
┌────────────┐
│ 应用层 │
└────┬───────┘
│ Sharding-JDBC
┌────┴───────┐
│ 订单库1 │
│ 订单库2 │
└───────────┘
👉 这样可以支持高并发、高可用、灵活扩展! 🚀
二 垂直分库分表
2.1 垂直分库(Vertical Database Partitioning)
垂直分库 主要是按照业务拆库,将不同的业务模块存储在不同的数据库实例中。
适用于大业务拆分,常见方案包括:
2.1.1 按业务拆库
示例:
- 用户库(user_db):存储用户基本信息(
users, profiles, auth_data
) - 订单库(order_db):存储订单信息(
orders, order_items, order_logs
) - 支付库(payment_db):存储支付数据(
transactions, payment_logs
)
优点:
✅ 降低数据库压力,减少不同业务之间的干扰
✅ 易于扩展,每个业务数据库可以单独扩容
✅ 提升安全性,不同业务数据可独立管理,权限更清晰
2.1.2 为什么在水平分库分表后又提了垂直分库?
虽然水平分库分表 可以解决 单表数据过大、查询变慢 的问题,但如果一个表的字段过多,查询时经常只需要部分字段,那么垂直分库分表 能进一步优化查询效率。
示例场景:
- 订单表(order) 可能包含
id, user_id, order_time, order_status, payment_info, shipping_address, logistics_info, order_items
等字段。 - 但大部分查询 仅需要
id, user_id, order_time, order_status
,无需支付信息、物流信息等。 - 解决方案:可以把
payment_info, shipping_address, logistics_info
拆分到 order_ext 表,提升查询效率。
因此,我们可以综合采用 水平+垂直分库分表 来最大化数据库的可扩展性和查询性能。
2.2 垂直分表(Vertical Table Partitioning)
垂直分表 是指 把同一个表中的字段拆分成多个表,通常用于拆分宽表(Wide Table)。
2.2.1 按字段频率拆分
如果一个表有 高频字段(经常查询) 和 低频字段(不常用),可以拆分:
- 高频表(核心表):存放最常用的数据
- 低频表(扩展表):存放补充信息
示例:订单表(orders)
(高频表) |
(低频表) |
id (PK) |
id (PK) |
user_id |
shipping_address |
order_status |
payment_info |
order_time |
logistics_info |
✅ 查询优化:大多数查询只访问 orders
表,提高效率
✅ 减少索引开销:索引只维护高频字段,提升查询性能
2.2.2 按数据大小拆分
如果表中有大字段(如 BLOB, TEXT, JSON
),影响查询效率,可以拆表存储。
示例:商品表(products)
(主表) |
(扩展表) |
id (PK) |
id (PK) |
name |
description (TEXT) |
price |
images (BLOB) |
✅ 提升查询性能:减少主表数据体积,加快查询速度
✅ 优化缓存:主表数据可缓存,扩展表数据可按需查询
2.2.3 按冷热数据拆分
如果表里有经常访问的数据(热数据) 和 历史数据(冷数据),可以拆表存储:
- hot_orders(近6个月订单)
- archived_orders(历史订单)
示例SQL
CREATE TABLE hot_orders LIKE orders;
CREATE TABLE archived_orders LIKE orders;
定期归档:
INSERT INTO archived_orders SELECT * FROM hot_orders WHERE order_time < NOW() - INTERVAL 6 MONTH;
DELETE FROM hot_orders WHERE order_time < NOW() - INTERVAL 6 MONTH;
✅ 提高查询效率:热点数据表更小,查询更快
✅ 减少主库压力:历史数据归档,不影响业务性能
2.3 水平 vs 垂直分库分表
对比项 |
水平分库分表 |
垂直分库分表 |
拆分方式 |
按数据范围拆分 |
按业务或字段拆分 |
适用场景 |
数据量过大,单表查询变慢 |
业务模块清晰,表结构复杂 |
查询优化 |
通过分片查询提升并行能力 |
通过减少查询字段提升效率 |
扩展性 |
适合高并发、分布式场景 |
适合业务隔离、高效查询 |
索引优化 |
每个分片索引独立维护 |
低频字段减少索引占用 |
高可用支持 |
需要分片路由和事务支持 |
业务数据库独立管理 |
运维难度 |
需要中间件支持(ShardingSphere) |
业务间依赖减少,管理简单 |
2.4 结合水平+垂直分库分表
在实际项目中,我们可以结合使用水平和垂直分库分表,例如:
- 按业务拆库(垂直分库):用户库、订单库、支付库
- 按范围拆表(水平分表):订单表按
user_id % N
进行分表 - 按高低频拆表(垂直分表):
orders
+orders_ext
- 按冷热数据拆分(归档策略):
hot_orders
+archived_orders
3 总结
分库分表方式 |
适用场景 |
优势 |
典型例子 |
水平分库 |
数据量大 |
负载均衡,支持分布式扩展 |
|
水平分表 |
单表过大 |
提高查询效率 |
|
垂直分库 |
业务独立 |
业务隔离,易扩展 |
|
垂直分表 |
宽表查询慢 |
读写优化,减少索引开销 |
|