数据库架构设计:数据切分与分区详解
数据库架构设计:数据切分与分区详解
随着业务量的增长,单一数据库最终会面临性能瓶颈。数据切分(Sharding)是将数据分散到多个数据库或服务器上,以分担负载、提高系统可扩展性的关键技术。
1. 什么是数据切分?
数据切分是指通过特定的规则,将存储在单个数据库中的数据分散到多个数据库中,从而分散单台设备的负载。主要有两种切分方式:垂直切分和水平切分。
2. 垂直切分 (Vertical Splitting)
垂直切分是按照业务或表将数据分散到不同的数据库上。
- 核心思想:将不同业务模块的表拆分到不同的数据库中。例如,一个电商系统可以拆分为用户库、商品库和订单库。
- 实施方式:基于业务逻辑进行拆分,耦合度低的业务模块适合此方法。
优点
- 业务逻辑清晰:每个库对应独立的业务模块,职责分明。
- 实施简单:拆分规则清晰,对应用程序改动较小。
- 易于维护:不同业务团队可以独立维护各自的数据库。
缺点
- 无法解决单表瓶颈:如果某个业务的单张表数据量巨大,垂直切分无法解决其性能问题。
- 可能存在跨库 Join:如果业务之间存在关联查询,会引入复杂性。
3. 水平切分 (Horizontal Splitting)
水平切分是根据表内数据的逻辑关系,将同一张表的数据按某种条件拆分到多个数据库中。
- 核心思想:将一张大表的数据行分散到多个库或表中。例如,将用户表按照用户 ID 的范围或哈希值进行拆分。
- 适用场景:
- 单表数据量巨大(例如,千万或亿级记录)。
- 表的访问非常频繁,并发压力大。
优点
- 解决单表瓶颈:有效分散单张大表的存储和访问压力。
- 提高系统稳定性:避免因单库故障导致整个业务不可用。
- 良好的扩展性:可以通过增加数据库节点来线性提升系统性能。
缺点
- 拆分规则复杂:需要设计合理的分片键(Shard Key)和路由算法,后期难以更改。
- 数据维护困难:数据分散在多处,查询、统计和维护变得复杂。
- 跨分片 Join 困难:通常需要应用层代码或中间件来处理跨库 Join。
4. 数据切分的共同挑战及解决方案
无论是垂直切分还是水平切分,都会引入分布式系统的一些共同挑战。
挑战 1:分布式事务
跨多个数据库的操作需要保证事务的一致性。
解决方案:
- 两阶段提交/三阶段提交 (2PC/3PC):传统分布式事务协议,但性能较差,实现复杂。
- TCC (Try-Confirm-Cancel):由业务代码自行实现
Try、Confirm、Cancel三个操作,对代码侵入性强。 - 本地消息表/可靠消息队列:通过消息队列(如 RocketMQ, Kafka)实现最终一致性,是目前业界主流方案。
- SOA/微服务架构:将跨库操作封装在服务层,由服务内部保证事务。
挑战 2:跨节点 Join、排序与分页
跨库查询无法直接使用 SQL 的 JOIN,聚合和排序操作也变得复杂。
解决方案:
- 应用层聚合:在应用程序中多次查询各个节点,然后将结果合并、排序。
- 数据异构到中间库:将需要 Join 的数据同步到一个专用的数据仓库或搜索引擎(如 Elasticsearch)中,用于复杂查询。
- 数据库中间件:使用如
MyCat、ShardingSphere等中间件,它们能够代理 SQL 请求,自动处理跨分片的查询和聚合。
挑战 3:多数据源管理
应用程序需要管理多个数据库连接。
解决方案:
- 数据库中间件:对应用层透明,应用只需连接中间件即可。
- 客户端分片库:在应用程序中集成Sharding-JDBC等客户端库,由其在代码层管理多数据源。
5. 表分区 (Table Partitioning)
表分区是一种在数据库内部对表进行逻辑拆分的技术,它与数据切分(Sharding)不同,后者是在数据库外部进行物理拆分。分区对于应用层是透明的。
以 Oracle 为例,分区可以将一张大表的数据存储在不同的物理段(分区)中,但在逻辑上仍是一张表。
Oracle 表分区类型
- 范围分区 (Range Partitioning):按列值的范围进行分区,常用于日期或连续的数字。
- 哈希分区 (Hash Partitioning):按列值的哈希值进行分区,用于均匀分布数据。
- 列表分区 (List Partitioning):按列值的离散列表进行分区,常用于状态、地区等字段。
- 复合分区 (Composite Partitioning):以上几种方式的组合。
分区表示例 (Range)
1
2
3
4
5
6
7
8
9
10
11
-- 创建一个按月分区的销售表
CREATE TABLE sales (
sale_id NUMBER,
sale_date DATE,
amount NUMBER
)
PARTITION BY RANGE (sale_date) (
PARTITION p_2023_01 VALUES LESS THAN (TO_DATE('2023-02-01', 'YYYY-MM-DD')),
PARTITION p_2023_02 VALUES LESS THAN (TO_DATE('2023-03-01', 'YYYY-MM-DD')),
PARTITION p_max VALUES LESS THAN (MAXVALUE)
);
分区管理
- 查看分区:
SELECT * FROM user_tab_partitions WHERE table_name = 'SALES'; - 查询分区数据:
SELECT * FROM sales PARTITION (p_2023_01); - 添加分区:
ALTER TABLE sales ADD PARTITION p_2023_03 VALUES LESS THAN (TO_DATE('2023-04-01', 'YYYY-MM-DD')); - 删除分区:
ALTER TABLE sales DROP PARTITION p_2023_01;
普通表转换为分区表
对于已存在的普通表(堆表),可以通过在线重定义(DBMS_REDEFINITION)或 CREATE TABLE AS SELECT 的方式将其转换为分区表。在线重定义可以在不影响业务的情况下完成转换。
分区索引
为了进一步提高了查询效率,可以为分区表创建索引。
- 本地索引 (Local Index):索引与其对应的表分区一一对应,管理简单。推荐使用。
1
CREATE INDEX idx_sales_date ON sales(sale_date) LOCAL;
- 全局索引 (Global Index):一个索引可以指向多个分区,管理复杂。仅在特定场景下使用。
注意:分区键的选择至关重要,通常应选择查询中最常用的过滤条件字段。
本文由作者按照 CC BY 4.0 进行授权