分库分表策略

发布于:2025-04-01 ⋅ 阅读:(71) ⋅ 点赞:(0)

目录

一 水平分库分表

1. 水平分库(Database Sharding)

1.1 水平分库策略

(1) 哈希分库(取模分库)

(2) 范围分库

(3) 一致性哈希分库

1.2 数据路由

(1) 应用层路由

(2) 代理层路由

2. 水平分表(Table Sharding)

2.1 水平分表策略

(1) 哈希取模分表

(2) 范围分表

2.2 水平分表查询

3. 水平分库 vs 水平分表

4. 结合水平分库+分表

二 垂直分库分表

2.1 垂直分库(Vertical Database Partitioning)

2.1.1 按业务拆库

2.1.2 为什么在水平分库分表后又提了垂直分库?

2.2 垂直分表(Vertical Table Partitioning)

2.2.1 按字段频率拆分

示例:订单表(orders)

2.2.2 按数据大小拆分

示例:商品表(products)

2.2.3 按冷热数据拆分

2.3 水平 vs 垂直分库分表

2.4 结合水平+垂直分库分表

3 总结


在高并发、高可用的业务场景中,分库分表(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_timeuser_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 = 101user_1
  • user_id = 202user_2
  • user_id = 303user_3
  • user_id = 404user_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. 结合水平分库+分表

实际业务中,我们通常会结合使用

  1. 先按业务分库(垂直拆分:user_db, order_db, payment_db
  2. 再水平分库(按 user_id 取模拆分多个数据库)
  3. 最后水平分表(订单表 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)

orders

(高频表)

orders_ext

(低频表)

id (PK)

id (PK)

user_id

shipping_address

order_status

payment_info

order_time

logistics_info

查询优化:大多数查询只访问 orders 表,提高效率
减少索引开销:索引只维护高频字段,提升查询性能

2.2.2 按数据大小拆分

如果表中有大字段(如 BLOB, TEXT, JSON),影响查询效率,可以拆表存储。

示例:商品表(products)

products

(主表)

products_detail

(扩展表)

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 总结

分库分表方式

适用场景

优势

典型例子

水平分库

数据量大

负载均衡,支持分布式扩展

订单库1, 订单库2, 订单库3

水平分表

单表过大

提高查询效率

orders_01, orders_02, orders_03

垂直分库

业务独立

业务隔离,易扩展

user_db, order_db, payment_db

垂直分表

宽表查询慢

读写优化,减少索引开销

orders, orders_ext


网站公告

今日签到

点亮在社区的每一天
去签到