1. 分区表基础概念
说实话,我刚接触分区表那会儿,也觉得这东西挺玄乎的。不就是把一张大表拆成几张小表吗?后来在项目里踩过坑,才真正理解了它的精髓。
今天咱们就来聊聊分区表最核心的几个概念。我会结合自己这些年摸爬滚打的经验,把这块讲透。
什么是表分区
表分区,说白了就是「分而治之」的思想。一张表数据量太大,查询慢、维护难,怎么办?把它按某种规则拆成多个物理片段,每个片段独立存储、独立管理。
但注意,从应用层看,它还是一张表。你写 SELECT * FROM orders,数据库会自动帮你定位到正确的分区。这就是透明性。
核心要点:分区是物理存储层面的拆分,逻辑层面仍然是完整的一张表。
我记得有一次,某电商平台的订单表到了 5 亿行,全表扫描一次要 40 多分钟。后来按月份做了范围分区,查询最近三个月的订单,秒级返回。这就是分区的威力。
分区键的选择
分区键选得好不好,直接决定了分区方案成败。我见过太多选错分区键导致性能反而下降的案例。
选分区键,我一般遵循这几个原则:
- 查询频率高:分区键要能覆盖大部分查询的 WHERE 条件
- 数据分布均匀:别让某个分区数据量特别大,其他分区空着
- 不易变更:分区键一旦确定,修改起来非常麻烦
- 有业务意义:比如时间、地域、用户 ID 这类天然有区分度的字段
我的经验:时间字段是最常用的分区键。为什么?因为大部分业务数据都有时间属性,而且查询通常也带时间范围。我做过的大项目,80% 以上都是用时间做分区键。
举个例子,订单表用 order_date 做分区键,查询「上个月的订单」就只需要扫描一个分区。如果用 order_id 做分区键,那查询条件里必须带上 order_id 范围才能走分区裁剪,否则就是全分区扫描。
分区类型详解
MySQL、PostgreSQL、Oracle 这些主流数据库,分区类型大同小异。我按最常见的四种来展开。
范围分区(RANGE)
这是最常用的一种。按连续的值范围来划分,典型的就是按时间。
CREATE TABLE orders (
order_id INT,
order_date DATE,
amount DECIMAL(10,2)
)
PARTITION BY RANGE (YEAR(order_date)) (
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
适用场景:时间序列数据、日志数据、历史归档数据。
我曾经帮一家金融公司做数据归档,他们的交易流水表按季度分区。每个季度结束后,直接把上个季度的分区设为只读,备份时只备份那个分区。效率提升了好几倍。
列表分区(LIST)
按离散的值列表来划分。比如按地区、按状态、按类型。
CREATE TABLE employees (
emp_id INT,
name VARCHAR(50),
region VARCHAR(20)
)
PARTITION BY LIST (region) (
PARTITION p_north VALUES IN ('北京', '天津', '河北'),
PARTITION p_south VALUES IN ('广东', '广西', '海南'),
PARTITION p_east VALUES IN ('上海', '江苏', '浙江'),
PARTITION p_other VALUES IN ('其他')
);
适用场景:数据有明显的地域或类别属性,且查询经常按这些属性过滤。
注意:列表分区要确保所有可能的值都有对应的分区。我曾经遇到过线上报错,就是因为新插入的数据找不到匹配的分区。所以建议加一个 DEFAULT 分区兜底。
哈希分区(HASH)
按哈希函数的结果来分配数据。适合数据分布不均匀,但又没有天然分区键的场景。
CREATE TABLE user_sessions (
session_id INT,
user_id INT,
login_time DATETIME
)
PARTITION BY HASH (user_id)
PARTITIONS 8;
适用场景:数据量大但查询条件不固定,或者想均匀分散 I/O 压力。
嗯,这里要注意:哈希分区的分区数最好是 2 的幂次方。为什么?因为哈希取模运算在 2 的幂次方下效率最高。这是底层实现决定的,咱们记住就好。
复合分区(COMPOSITE)
先按一种方式分区,再在每个分区内按另一种方式子分区。说白了就是分区套分区。
CREATE TABLE logs (
log_id INT,
log_date DATE,
log_level VARCHAR(10),
message TEXT
)
PARTITION BY RANGE (YEAR(log_date))
SUBPARTITION BY HASH (log_level)
SUBPARTITIONS 4 (
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025)
);
适用场景:数据量极大(百亿级别),单一分区维度不够用。比如先按年分区,再按日志级别哈希子分区。
我个人习惯是,能不用复合分区就不用。为什么?管理复杂度翻倍。除非数据量真的到了非用不可的地步,否则单层分区就够了。
知识体系总览
下面这张图,是我梳理的分区表核心知识结构。你看一眼,心里就有谱了。
避坑指南
最后,分享几个我踩过的坑,希望能帮你少走弯路。
我曾经犯过的错:
- 分区数设得太多(比如 1024 个),结果每个分区文件都很小,反而增加了文件系统开销。建议分区数控制在 50 以内。
- 用字符串字段做分区键,导致分区裁剪失效。分区键尽量用整数或日期类型。
- 忘了给分区表加索引。分区表每个分区独立,索引也要在每个分区上建。全局索引和本地索引的区别,后面章节会细讲。
你想想看,分区表其实就是一个「拆」字。拆得好,性能翻倍;拆不好,维护成本暴增。选对分区键、选对分区类型,这两步走对了,后面就顺了。
好了,这一章就到这里。记住我今天讲的这几个核心概念,后面咱们聊备份还原的时候,你就知道为什么分区表备份能那么快了。