课程
无论您是在构建以客户为中心的应用,还是在运行复杂的分析管道,有一件事非常明确:您如何存储和访问数据,直接决定了性能的成败。
我亲眼见过,当数据被合理组织后,系统能顺畅许多——这正是数据分区发挥作用的地方。它是一种可以显著提升查询速度、存储效率和可扩展性的技术。
在本文中,我将解释什么是数据分区、有哪些常见类型,以及我在实践中总结出的最佳做法。
什么是数据分区?
数据分区是一种将大型数据集拆分为更小、可管理块(称为分区)的技术。每个分区包含数据的一个子集,并分布在多个节点或服务器上。虽然这些分区在逻辑上属于同一数据集,但可以作为独立的表进行存储、查询和管理。
数据分区可以提升数据库的性能和可扩展性。比如,在整张表中搜索某个数据点所需时间更长、资源更多,而在特定分区中搜索则更快。这也是为什么要将数据按分区存储。
数据分区的类型
数据分区包含多种类型,每种都有独特的策略和优势。本节将介绍最常见的类型。
水平分区
与其把所有数据存放在一张表中,水平分区是按行拆分数据,即将不同的行集合作为分区存储。
在水平分区中,所有分区的列集合相同,但包含的行组不同。
- 示例: 假设您有一个包含三个国家多个州的天气数据集。您可以按国家拆分行,将其划分为三张表。g the rows by each country.
|
Temperature |
State |
Country |
Season |
|
-30° F |
Alaska |
USA |
Winter |
|
48.2° F |
Queensland |
Australia |
Summer |
|
100° F |
Andhra Pradesh |
India |
Summer |
|
35° F |
California |
USA |
Winter |
|
96° F |
Texas |
USA |
Summer |
|
62° F |
Tasmania |
Australia |
Winter |
按国家进行水平分区:
|
Temperature |
State |
Country |
Season |
|
-30° F |
Alaska |
USA |
Winter |
|
35° F |
California |
USA |
Winter |
|
35° F |
California |
USA |
Winter |
|
Temperature |
State |
Country |
Season |
|
48.2° F |
Queensland |
Australia |
Summer |
|
62° F |
Tasmania |
Australia |
Winter |
|
Temperature |
State |
Country |
Season |
|
100° F |
Andhra Pradesh |
India |
Summer |
垂直分区
垂直分区是按列划分数据,因此每个分区包含相同数量的行,但列更少。
分区键或主键列会存在于每个分区中,以维护逻辑关联。
当需要将敏感信息与常规数据分开存储时,垂直分区非常常见。它允许将敏感列保存在一个分区,将普通数据保存在另一个分区。
另一个常见用例是将更新频繁的列分在一个分区,其他列放在另一个分区。
- 示例: 若下表中的员工手机号与薪资列为机密,我们就e table accordingly.
|
Emp_id |
Emp_name |
Designation |
Emp_email |
Emp_phone |
Emp_salary |
|
z0yu9h |
Alex |
Data scientist |
alex@yoho.com |
+101 890 456 |
250K |
|
f8o5pj |
Adam |
Data engineer |
adam@prodgy.com |
+630 789 365 |
230K |
|
z006yu |
Matt |
Data analyst |
matt@oltk.com |
+857 342 937 |
166K |
|
a6u895 |
Elina |
Ops engineer |
elina@wal.com |
+892 494 782 |
190K |
|
g68w90 |
Joseph |
Software engineer |
joseph@yoho.com |
+852 603 389 |
210K |
Emp_id 是上述表中的唯一标识,因此所有分区都会包含它。
分区 1:
|
Emp_id |
Emp_name |
Designation |
Emp_email |
|
z0yu9h |
Alex |
Data scientist |
alex@yoho.com |
|
f8o5pj |
Adam |
Data engineer |
adam@prodgy.com |
|
z006yu |
Matt |
Data analyst |
matt@oltk.com |
|
a6u895 |
Elina |
Ops engineer |
elina@wal.com |
|
g68w90 |
Joseph |
Software engineer |
joseph@yoho.com |
分区 2:
|
Emp_id |
Emp_phone |
Emp_salary |
|
z0yu9h |
+101 890 456 |
250K |
|
f8o5pj |
+630 789 365 |
230K |
|
z006yu |
+857 342 937 |
166K |
|
a6u895 |
+892 494 782 |
190K |
|
g68w90 |
+852 603 389 |
210K |
范围分区
范围分区根据某一列的取值范围来拆分数据。
通常,每个分区对键列都有下界和上界,落在该范围内的记录会被分配到对应的分区。
- 示例:假设销售数据覆盖 2021 至 2023 年。我们可以按年份分区,使 2021 到 2022 年的销售落在一个分区,2022 到 2023 年的销售落在另一个分区。
|
Product_id |
Date |
Profit |
|
z0yu9h |
2021-03-17 |
$100K |
|
f8o5pj |
2022-04-04 |
$234K |
|
z006yu |
2022-09-13 |
$789K |
|
a6u895 |
2021-07-23 |
$237K |
|
g68w90 |
2022-02-26 |
$2345K |
分区 1:
|
product_id |
Date |
Profit |
|
z0yu9h |
2021-03-17 |
$100K |
|
a6u895 |
2021-07-23 |
$237K |
分区 2:
|
product_id |
Date |
Profit |
|
f8o5pj |
2022-04-04 |
$234K |
|
z006yu |
2022-09-13 |
$789K |
哈希分区
对分区键应用哈希函数,其输出决定记录应存放到哪个分区。在这种技术中,分区数量需事先确定。
将哈希值除以分区数所得的余数,决定该记录应存入的分区。来看一个例子:
- 示例:对于 ID 为
z0yu9h的产品,若hash(‘z0yu9h’) = 101且分区数 = 4,则 101%4 = 1。因此,这条记录被存入第一个分区。
通过这种方式,对分区键列计算哈希值并除以分区数,结果用于确定最终分区。
列表分区
该过程与范围分区类似,但列表分区不是按取值范围划分,而是基于预定义的一组取值来划分数据。
以温度表为例。
|
Temperature |
State |
Country |
Season |
|
30° F |
Alaska |
USA |
Winter |
|
48.2° F |
Queensland |
Australia |
Summer |
|
100° F |
Andhra Pradesh |
India |
Summer |
|
35° F |
California |
USA |
Winter |
|
96° F |
Texas |
USA |
Summer |
|
62° F |
Tasmania |
Australia |
Winter |
在上述数据中,“Season”列的取值列表为 [‘winter’, ‘summer’]。因此,按季节的列表分区如下:
分区 1:
|
Temperature |
State |
Country |
Season |
|
30° F |
Alaska |
USA |
Winter |
|
35° F |
California |
USA |
Winter |
|
62° F |
Tasmania |
Australia |
Winter |
分区 2:
|
Temperature |
State |
Country |
Season |
|
48.2° F |
Queensland |
Australia |
Summer |
|
100° F |
Andhra Pradesh |
India |
Summer |
|
96° F |
Texas |
USA |
Summer |
复合分区
复合分区是两种分区技术的组合。先用一种策略对表进行划分,再对每个分区使用另一种技术进行进一步划分。
分区策略经常出现在技术面试中——参见2025 年最常见 DBA 面试题。
数据分区的使用场景
了解了不同的分区策略后,我们再看看数据分区在何时最有用。
分布式数据库
分布式系统由物理上位于不同服务器、通过网络连接的节点组成。示例包括 Apache Cassandra、Amazon DynamoDB 和 Google Bigtable。
这些系统内部使用数据分区来组织并在各节点间分配数据。
OLAP 操作
OLAP将多维数据表示为数据立方体,允许用户从不同角度探索数据。尽管这些立方体代表预聚合数据,但底层数据量依然庞大,因此仍需要通过数据分区对查询进一步优化。
例如,应用立方体分区(一种数据分区技术)可根据特定条件将数据划分为更小的立方体。查询数据时,可跳过无关的立方体,仅在特定立方体上执行搜索,从而减少 I/O 操作。
同样,也可以应用维度分区,按层级、层次或列来拆分数据。
日志管理
日志包含系统中发生的事件、交易与操作信息。它们用于追踪故障、缺陷、边界情况,甚至成功消息。
对日志进行分区可以加速调试,帮助更快获得所需信息。例如,若您按时间段对日志进行了分区,就可以按日期、周、月或年轻松检索。
机器学习流水线
在机器学习场景中,通常将数据划分为训练集、验证集和测试集。这样可以确保测试集的信息不会在训练过程中泄露给模型,从而避免过拟合。
此外,如果训练数据非常庞大,单机无法处理并将全部数据输入模型,因此会通过数据分区将数据拆分为块分批输入模型。
实现数据分区的工具
数据分区可以在 SQL 和 NoSQL 数据库中实现。本节将分别讨论。
SQL 数据库
PostgreSQL
PostgreSQL内置支持范围、列表和哈希分区。例如,以下查询为每个年度创建一个分区,以高效访问各财年销售利润与销售总额。
CREATE TABLE sales(
city_id int not null,
sales_year date not null,
total_sales int,
sales_profits int
) PARTITION BY RANGE (sales_year);
MySQL
MySQL支持多种水平分区类型,如范围、列表、哈希和键分区。目前(8.4 版本)尚不支持垂直分区。
下面的 MySQL 查询将员工数据按入职日期划分为 p1、p2 和 p3 三个分区。所有 2000 年之前入职的员工属于 p1 分区;2000 至 2010 年入职的员工属于 p2 分区;2010 至 2020 年入职的员工属于 p3 分区。
CREATE TABLE employee(
firstname VARCHAR(25) NOT NULL,
username VARCHAR(16) NOT NULL,
email VARCHAR(35),
salary INT,
joined DATE NOT NULL
)
PARTITION BY RANGE( YEAR(joined) ) (
PARTITION p1 VALUES LESS THAN (2000),
PARTITION p2 VALUES LESS THAN (2010),
PARTITION p3 VALUES LESS THAN (2020)
);
Microsoft SQL Server
SQL Server通过分区函数和分区方案提供分区功能。分区函数指定如何拆分数据,分区方案决定分区将存储到哪个文件组。
在以下查询中:
- 函数名:
sales_by_year - 方案名:
sales_filegroup - 文件组名:
fg01、fg02、fg03、fg04
分区函数:
create partition function sales_by_year (date)
as range right for values
('2024-01-01', '2024-02-01', '2024-03-01', ‘2024-04-01’);
分区方案:
CREATE PARTITION SCHEME sales_filegroup
AS PARTITION sales_by_year
TO (fg01, fg02, fg03, fg04);
在这里,文件存储数据如下:
fg01- 日期早于 ‘2024-01-01’fg02- ‘2024-01-01’ 至 ‘2024-01-31’fg03- ‘2024-02-01’ 至 ‘2024-02-29’fg04- ‘2024-03-01’ 至 ‘2024-03-31’
由于在查询中指定了 range right,每个边界日期会被包含在下一个分区中。
如果您正在使用 SQL Server,这份SQL Server 教程将带您了解管理分区数据的关键概念和工具。若需系统化学习路径,SQL Server 基础学习路径介绍了构建高性能 SQL 数据库所需的核心功能。
NoSQL 数据库
Amazon DynamoDB
DynamoDB在内部自动处理分区。当您向 DynamoDB 表写入一条项目时,它会经过请求路由器。路由器解析分区键并应用哈希函数。哈希函数的输出决定记录将存储到哪个特定分区。
整个过程在 DynamoDB 内部自动完成,用户无需关心哈希函数或分区如何被管理。
Mongodb
MongoDB通过分片(sharding)的概念在服务器之间分布数据。数据基于分片键进行分区,应谨慎选择分片键以避免分布不均。
通常,分片键应具有高基数,并且应是查询中经常使用的列。这些MongoDB 面试题几乎涵盖了包括分片在内的所有重要主题。
Apache Cassandra
Cassandra 的分区器是一种哈希算法,用于确定集群中哪个节点应存储特定分区。将分区键传入该算法以生成令牌(token),然后将该令牌与节点分配的令牌值进行比较,最终将数据存储到匹配该令牌的节点上。
常见的分区算法包括 Murmur3Partitioner、RandomPartitioner 和 ByteOrderedPartitioner。
如何实现数据分区?
下面来讨论正确实施数据分区所需的步骤。
步骤 1:了解数据与访问模式
全面评估应用如何查询数据库、哪些数据经常被访问、数据规模及性能瓶颈。这些洞察将帮助您为分区设定明确目标,并识别在数据操作中经常涉及的关键列。
步骤 2:选择分区技术
我们已经介绍了常见技术,以下是决策依据:
- 水平分区: 当单表需要存储数百万乃至数十亿行,导致内存问题时,应选择水平分区。它将不同的行集合存放在不同的表中。
- 垂直分区: 如果有一组列被频繁访问,而其他列偶尔被查询,您可以实施垂直分区,依据访问模式分别存储这些列。
- 混合方式: 当数据集非常庞大,既需要减少行数,又要对某些列的查询进行优化时,应选择混合方式。它结合了水平分区与垂直分区的优势。
步骤 3:创建分区
以下是一个使用哈希分区创建分区的 SQL 示例。
CREATE TABLE employees (
emp_id INT NOT NULL,
name VARCHAR(30),
hired DATE NOT NULL DEFAULT '2024-01-01',
job_id INT,
location_id INT
)
PARTITION BY HASH(location_id)
PARTITIONS 4;
在上述代码中,将“location_id”列传入哈希函数,其输出决定记录应分配到的分区。代码行 partitions 4 指定了要创建的分区总数。
收藏这份便捷的SQL 基础速查表,在实现分区时随时查阅核心 SQL 语法。
步骤 4:监控并优化分区
- 持续监控数据操作,确保分区是最新且有效的。
- 随着应用扩展,您可能需要增加分区数量,或选择不同的方法(如混合分区)以适应变化的需求。
- 确保分区大小与策略有利于数据库上的高频操作。
- 确保数据在分区间分布均衡;否则,可能需要进行再均衡。
数据分区的挑战与注意事项
如同软件世界中的一切优化一样,数据分区也伴随着权衡与挑战。以下是您应考虑的方面:
- 分区倾斜: 分区倾斜指少数分区承载了更多数据与处理工作,而其他分区闲置。为避免此问题,应选择能使数据在各分区间均匀分布的合适分区键与策略。
- 维护开销: 随着数据增长,您需要重新评估现有的分区策略是否仍与当前数据相匹配;若不匹配,可能需要重新分区或合并分区。
- 数据迁移: 在将数据迁移到新架构时,需要格外注意,确保分区与新环境兼容。此外,在分区之间移动数据会带来诸如跨分片的一致性管理等复杂性。
- 学习曲线陡峭: 数据分区会为系统或云架构增加复杂性。分区数据并不易于查询、迁移或分析,对于初学者来说学习曲线较为陡峭。
数据分区的最佳实践
如您所见,随着数据量增长,数据分区会变得复杂。但落实以下实践可确保在提升效率的同时,不牺牲性能。
- 让分区与查询模式对齐: 选择合适的分区策略与分区键能显著提升数据库性能。了解数据库中最常见的数据操作与查询模式,选择在查询中经常使用、同时能确保数据均匀分布的分区键。
- 定期监控: 当今应用是动态的,会随趋势与需求不断演进。因此,务必定期监控分区,使其与当前访问模式保持一致,并在必要时重新分区。
- 减少开销: 在大规模场景中手动管理分区很复杂。然而,像 Apache Cassandra 和 Amazon DynamoDB 这样的 NoSQL 数据库会自动化这一过程,无需大量额外工作。此外,一些自动化工具会随数据规模调整分区,按需进行重新分区或合并分区。
结语
随着数据增长,实施有效的数据分区对维持性能与可扩展性至关重要。选择合适的策略,您可以降低查询延迟、优化存储并简化维护——这是一项能带来可衡量影响的务实举措。
若想深入了解分区如何助力分布式系统扩展,请参阅这篇Kafka 分区指南。要理解关系型结构如何影响分区决策,可先从这门关系型数据库课程开始。
Srujana 是一名自由职业技术写作者,拥有计算机科学四年制学位。她擅长撰写各类主题的内容,包括数据科学、云计算、开发、编程、安全等众多领域。她热爱经典文学,也喜欢探索新的旅行目的地。
