前置知识: PostgreSQL

分区表

2 min高级

PostgreSQL分区表:范围分区、列表分区、哈希分区的语法、管理与分区裁剪

1. 声明式分区

PostgreSQL 10+ 支持声明式分区,语法简洁。

2. 范围分区

CREATE TABLE orders (
    id BIGSERIAL,
    order_date DATE NOT NULL,
    amount DECIMAL(10,2)
) PARTITION BY RANGE (order_date);

CREATE TABLE orders_2026_q1 PARTITION OF orders
    FOR VALUES FROM ('2026-01-01') TO ('2026-04-01');
CREATE TABLE orders_2026_q2 PARTITION OF orders
    FOR VALUES FROM ('2026-04-01') TO ('2026-07-01');
CREATE TABLE orders_default PARTITION OF orders DEFAULT;

3. 列表分区

CREATE TABLE customers (
    id SERIAL,
    name VARCHAR(100),
    region VARCHAR(20)
) PARTITION BY LIST (region);

CREATE TABLE customers_east PARTITION OF customers
    FOR VALUES IN ('华东', '华北');
CREATE TABLE customers_south PARTITION OF customers
    FOR VALUES IN ('华南', '西南');

4. 哈希分区

CREATE TABLE logs (
    id BIGSERIAL,
    message TEXT,
    created_at TIMESTAMP DEFAULT NOW()
) PARTITION BY HASH (id);

CREATE TABLE logs_0 PARTITION OF logs FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE logs_1 PARTITION OF logs FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE logs_2 PARTITION OF logs FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE logs_3 PARTITION OF logs FOR VALUES WITH (MODULUS 4, REMAINDER 3);

5. 分区管理

-- 添加分区
CREATE TABLE orders_2026_q3 PARTITION OF orders
    FOR VALUES FROM ('2026-07-01') TO ('2026-10-01');

-- 分离分区
ALTER TABLE orders DETACH PARTITION orders_2026_q1;

-- 附加分区
ALTER TABLE orders ATTACH PARTITION orders_2026_q1
    FOR VALUES FROM ('2026-01-01') TO ('2026-04-01');

-- 删除分区(数据也删除)
DROP TABLE orders_2026_q1;

6. 分区裁剪

-- 查询自动裁剪不需要的分区
EXPLAIN SELECT * FROM orders WHERE order_date >= '2026-04-01';
-- 只扫描 orders_2026_q2

-- 确认裁剪
SET enable_partition_pruning = ON;

范围分区

换行写法:创建范围分区主表 CREATE TABLE <表名> (<列定义>) PARTITION BY RANGE (<列名>)

-- 创建按日期范围分区的订单表
CREATE TABLE orders (
    id BIGSERIAL,
    order_date DATE NOT NULL,
    customer_id INT NOT NULL,
    amount DECIMAL(10, 2)
) PARTITION BY RANGE (order_date);

换行写法:创建范围分区子表 CREATE TABLE <子表名> PARTITION OF <父表> FOR VALUES FROM (<起始>) TO (<结束>)

-- 创建 2024 年 1 月的分区
CREATE TABLE orders_2024_01 PARTITION OF orders
    FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');

换行写法:创建多个范围分区子表 CREATE TABLE <子表名> PARTITION OF <父表> FOR VALUES FROM (<起始>) TO (<结束>)

-- 创建 2024 年 2 月的分区
CREATE TABLE orders_2024_02 PARTITION OF orders
    FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');

列表分区

换行写法:创建列表分区主表 CREATE TABLE <表名> (<列定义>) PARTITION BY LIST (<列名>)

-- 创建按地区列表分区的用户表
CREATE TABLE users (
    id BIGSERIAL,
    username VARCHAR(50),
    region VARCHAR(20)
) PARTITION BY LIST (region);

换行写法:创建列表分区子表 CREATE TABLE <子表名> PARTITION OF <父表> FOR VALUES IN (<值>[, <值>...])

-- 创建华北地区的分区
CREATE TABLE users_north PARTITION OF users
    FOR VALUES IN ('北京', '天津', '河北');

换行写法:创建多个列表分区子表 CREATE TABLE <子表名> PARTITION OF <父表> FOR VALUES IN (<值>[, <值>...])

-- 创建华南地区的分区
CREATE TABLE users_south PARTITION OF users
    FOR VALUES IN ('广东', '广西', '海南');

哈希分区

换行写法:创建哈希分区主表 CREATE TABLE <表名> (<列定义>) PARTITION BY HASH (<列名>)

-- 创建按用户 ID 哈希分区的用户表
CREATE TABLE users (
    id BIGSERIAL,
    username VARCHAR(50),
    email VARCHAR(100)
) PARTITION BY HASH (id);

换行写法:创建哈希分区子表 CREATE TABLE <子表名> PARTITION OF <父表> FOR VALUES WITH (MODULUS <模数>, REMAINDER <余数>)

-- 创建哈希余数为 0 的分区
CREATE TABLE users_0 PARTITION OF users
    FOR VALUES WITH (MODULUS 4, REMAINDER 0);

换行写法:创建多个哈希分区子表 CREATE TABLE <子表名> PARTITION OF <父表> FOR VALUES WITH (MODULUS <模数>, REMAINDER <余数>)

-- 创建哈希余数为 1 的分区
CREATE TABLE users_1 PARTITION OF users
    FOR VALUES WITH (MODULUS 4, REMAINDER 1);

分区管理

单行写法:查看分区表信息 SELECT <列名> FROM pg_inherits WHERE <条件>

-- 查看分区表的子表
SELECT inhrelid::regclass AS child_table
FROM pg_inherits
WHERE inhparent = 'orders'::regclass;

单行写法:查看分区表结构 SELECT <列名> FROM pg_partitioned_table WHERE <条件>

-- 查看分区表的结构信息
SELECT partrelid::regclass AS table_name, partstrat AS strategy
FROM pg_partitioned_table;

单行写法:分离分区 ALTER TABLE <父表> DETACH PARTITION <子表名>

-- 分离分区使其成为独立表
ALTER TABLE orders DETACH PARTITION orders_2024_01;

单行写法:附加分区 ALTER TABLE <父表> ATTACH PARTITION <子表名> FOR VALUES FROM (<起始>) TO (<结束>)

-- 附加分区到父表
ALTER TABLE orders ATTACH PARTITION orders_2024_01
    FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');

单行写法:删除分区子表 DROP TABLE <子表名>

-- 删除分区子表
DROP TABLE orders_2024_01;

换行写法:创建默认分区 CREATE TABLE <子表名> PARTITION OF <父表> DEFAULT

-- 创建默认分区存放不匹配的数据
CREATE TABLE users_default PARTITION OF users DEFAULT;

分区索引

单行写法:在父表创建索引 CREATE INDEX <索引名> ON <表名>(<列名>)

-- 在父表创建索引自动应用到所有分区
CREATE INDEX idx_orders_date ON orders(order_date);

单行写法:在子表创建索引 CREATE INDEX <索引名> ON <子表名>(<列名>)

-- 在单个分区子表创建索引
CREATE INDEX idx_orders_2024_01_date ON orders_2024_01(order_date);

分区裁剪

单行写法:查询触发分区裁剪 SELECT * FROM <分区表> WHERE <分区列> <操作符> <值>

-- 查询条件触发分区裁剪只扫描匹配分区
SELECT * FROM orders WHERE order_date = '2024-01-15';

单行写法:范围查询触发分区裁剪 SELECT * FROM <分区表> WHERE <分区列> BETWEEN <值1> AND <值2>

-- 范围查询触发分区裁剪
SELECT * FROM orders WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31';

单行写法:查看查询计划 EXPLAIN SELECT * FROM <分区表> WHERE <条件>

-- 查看查询是否触发分区裁剪
EXPLAIN SELECT * FROM orders WHERE order_date = '2024-01-15';