数据分区 #
一、分区概述 #
1.1 分区概念 #
text
数据分区
┌─────────────────────────────────────────────────────────────┐
│ │
│ 分区作用: │
│ ├── 提高查询性能 │
│ ├── 简化数据管理 │
│ ├── 支持数据归档 │
│ └── 优化数据放置 │
│ │
│ CockroachDB 分区特点: │
│ ├── 基于Range的分区 │
│ ├── 自动分区管理 │
│ ├── 分区与Zone配置结合 │
│ └── 支持多级分区 │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 分区类型 #
| 类型 | 说明 |
|---|---|
| Range分区 | 按范围分区 |
| Hash分区 | 按哈希分区 |
| List分区 | 按列表分区 (通过Range模拟) |
二、Range分区 #
2.1 创建Range分区 #
sql
-- 创建分区表
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id BIGINT,
order_no VARCHAR(50),
amount DECIMAL(10, 2),
status VARCHAR(20),
created_at TIMESTAMP DEFAULT NOW()
) PARTITION BY RANGE (created_at);
-- 创建分区
CREATE TABLE orders_2023 PARTITION OF orders
FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');
CREATE TABLE orders_2024 PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
CREATE TABLE orders_2025 PARTITION OF orders
FOR VALUES FROM ('2025-01-01') TO ('2026-01-01');
-- 默认分区
CREATE TABLE orders_default PARTITION OF orders
DEFAULT;
2.2 多列Range分区 #
sql
-- 多列分区
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
event_type VARCHAR(50),
event_date DATE,
data JSONB
) PARTITION BY RANGE (event_type, event_date);
-- 创建分区
CREATE TABLE events_login_2024 PARTITION OF events
FOR VALUES FROM ('login', '2024-01-01') TO ('login', '2025-01-01');
CREATE TABLE events_click_2024 PARTITION OF events
FOR VALUES FROM ('click', '2024-01-01') TO ('click', '2025-01-01');
2.3 分区管理 #
sql
-- 添加新分区
CREATE TABLE orders_2026 PARTITION OF orders
FOR VALUES FROM ('2026-01-01') TO ('2027-01-01');
-- 删除分区 (保留数据)
ALTER TABLE orders DETACH PARTITION orders_2023;
-- 删除分区和数据
DROP TABLE orders_2023;
-- 合并分区
-- 先分离,再删除,最后重新创建
ALTER TABLE orders DETACH PARTITION orders_2023;
ALTER TABLE orders DETACH PARTITION orders_2024;
-- 创建合并后的分区
CREATE TABLE orders_2023_2024 PARTITION OF orders
FOR VALUES FROM ('2023-01-01') TO ('2025-01-01');
三、Hash分区 #
3.1 创建Hash分区 #
sql
-- 创建Hash分区表
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100),
email VARCHAR(100),
region VARCHAR(50)
) PARTITION BY HASH (id);
-- 创建Hash分区
CREATE TABLE users_p0 PARTITION OF users
FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE users_p1 PARTITION OF users
FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE users_p2 PARTITION OF users
FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE users_p3 PARTITION OF users
FOR VALUES WITH (MODULUS 4, REMAINDER 3);
3.2 Hash分区特点 #
text
Hash分区特点
┌─────────────────────────────────────────────────────────────┐
│ │
│ 优点: │
│ ├── 数据均匀分布 │
│ ├── 避免热点 │
│ └── 适合等值查询 │
│ │
│ 缺点: │
│ ├── 范围查询效率低 │
│ ├── 分区调整复杂 │
│ └── 不适合时间序列数据 │
│ │
│ 适用场景: │
│ ├── 用户数据 │
│ ├── 订单数据 │
│ └── 需要均匀分布的数据 │
│ │
└─────────────────────────────────────────────────────────────┘
四、分区与Zone配置 #
4.1 分区级别Zone配置 #
sql
-- 为分区配置Zone
ALTER TABLE orders_2023 CONFIGURE ZONE USING
num_replicas = 3,
gc.ttlseconds = 2592000; -- 30天
ALTER TABLE orders_2024 CONFIGURE ZONE USING
num_replicas = 5,
gc.ttlseconds = 7776000; -- 90天
-- 为分区配置放置策略
ALTER TABLE orders_2023 CONFIGURE ZONE USING
constraints = '[+region=archive]';
ALTER TABLE orders_2024 CONFIGURE ZONE USING
constraints = '[+region=us-east]';
4.2 数据生命周期管理 #
sql
-- 创建按时间分区的表
CREATE TABLE logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
log_level VARCHAR(10),
message TEXT,
created_at TIMESTAMP DEFAULT NOW()
) PARTITION BY RANGE (created_at);
-- 创建分区并配置不同的TTL
CREATE TABLE logs_current PARTITION OF logs
FOR VALUES FROM (NOW() - INTERVAL '7 days') TO (NOW())
WITH (ttl = '7 days');
CREATE TABLE logs_recent PARTITION OF logs
FOR VALUES FROM (NOW() - INTERVAL '30 days') TO (NOW() - INTERVAL '7 days')
WITH (ttl = '30 days');
-- 归档旧数据
CREATE TABLE logs_archive PARTITION OF logs
FOR VALUES FROM (MINVALUE) TO (NOW() - INTERVAL '30 days')
WITH (ttl = '365 days');
五、分区查询 #
5.1 分区裁剪 #
sql
-- 分区裁剪: 只扫描相关分区
-- 查询特定时间范围的数据
SELECT * FROM orders
WHERE created_at BETWEEN '2024-01-01' AND '2024-06-30';
-- 只扫描 orders_2024 分区
-- 查看执行计划
EXPLAIN SELECT * FROM orders
WHERE created_at BETWEEN '2024-01-01' AND '2024-06-30';
-- 查看分区信息
SELECT * FROM pg_class WHERE relname LIKE 'orders%';
5.2 分区信息查询 #
sql
-- 查看表分区
SELECT
parent.relname AS table_name,
child.relname AS partition_name,
pg_get_expr(child.relpartbound, child.oid) AS partition_expr
FROM pg_class parent
JOIN pg_inherits i ON parent.oid = i.inhparent
JOIN pg_class child ON i.inhrelid = child.oid
WHERE parent.relname = 'orders';
-- 查看分区大小
SELECT
table_name,
pg_size_pretty(pg_total_relation_size(table_name::regclass)) AS size
FROM (
SELECT child.relname AS table_name
FROM pg_class parent
JOIN pg_inherits i ON parent.oid = i.inhparent
JOIN pg_class child ON i.inhrelid = child.oid
WHERE parent.relname = 'orders'
) t;
六、分区最佳实践 #
6.1 分区设计原则 #
text
分区设计原则
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 选择合适的分区键 │
│ ├── 时间序列数据: 时间列 │
│ ├── 用户数据: 用户ID │
│ └── 地理数据: 区域列 │
│ │
│ 2. 分区粒度 │
│ ├── 不宜过细: 管理复杂 │
│ ├── 不宜过粗: 性能差 │
│ └── 建议: 按月或按季度 │
│ │
│ 3. 分区数量 │
│ ├── 考虑数据量 │
│ ├── 考虑查询模式 │
│ └── 建议: 单表不超过100个分区 │
│ │
│ 4. 分区维护 │
│ ├── 定期创建新分区 │
│ ├── 归档或删除旧分区 │
│ └── 监控分区大小 │
│ │
└─────────────────────────────────────────────────────────────┘
6.2 分区示例 #
sql
-- 完整的分区表设计
CREATE TABLE transactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id BIGINT NOT NULL,
transaction_type VARCHAR(20),
amount DECIMAL(20, 2),
status VARCHAR(20),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
) PARTITION BY RANGE (created_at);
-- 创建分区函数
CREATE OR REPLACE FUNCTION create_monthly_partition(
base_table TEXT,
start_date DATE,
end_date DATE
) RETURNS VOID AS $$
DECLARE
partition_name TEXT;
BEGIN
partition_name := base_table || '_' || to_char(start_date, 'YYYY_MM');
EXECUTE format(
'CREATE TABLE %I PARTITION OF %I
FOR VALUES FROM (%L) TO (%L)',
partition_name,
base_table,
start_date,
end_date
);
END;
$$ LANGUAGE plpgsql;
-- 创建分区
SELECT create_monthly_partition('transactions', '2024-01-01', '2024-02-01');
SELECT create_monthly_partition('transactions', '2024-02-01', '2024-03-01');
SELECT create_monthly_partition('transactions', '2024-03-01', '2024-04-01');
-- 配置Zone
ALTER TABLE transactions_2024_01 CONFIGURE ZONE USING
num_replicas = 3,
gc.ttlseconds = 7776000; -- 90天
-- 创建索引
CREATE INDEX idx_transactions_user_id ON transactions (user_id, created_at);
七、分区维护 #
7.1 自动分区管理 #
sql
-- 创建自动分区管理存储过程
CREATE OR REPLACE FUNCTION manage_partitions()
RETURNS VOID AS $$
DECLARE
partition_date DATE;
partition_name TEXT;
BEGIN
-- 创建下个月的分区
partition_date := DATE_TRUNC('month', NOW() + INTERVAL '1 month');
partition_name := 'transactions_' || to_char(partition_date, 'YYYY_MM');
IF NOT EXISTS (
SELECT 1 FROM pg_class WHERE relname = partition_name
) THEN
EXECUTE format(
'CREATE TABLE %I PARTITION OF transactions
FOR VALUES FROM (%L) TO (%L)',
partition_name,
partition_date,
partition_date + INTERVAL '1 month'
);
END IF;
-- 可以添加删除旧分区的逻辑
END;
$$ LANGUAGE plpgsql;
-- 定期执行 (可以使用cron或其他调度工具)
SELECT manage_partitions();
7.2 分区监控 #
sql
-- 监控分区大小
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS size
FROM pg_tables
WHERE tablename LIKE 'transactions_%'
ORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC;
-- 监控分区数据量
SELECT
tableoid::regclass AS partition,
COUNT(*) AS row_count
FROM transactions
GROUP BY tableoid
ORDER BY partition;
八、总结 #
分区要点:
| 特性 | 说明 |
|---|---|
| Range分区 | 按范围分区 |
| Hash分区 | 按哈希分区 |
| 分区裁剪 | 优化查询性能 |
| Zone配置 | 分区级别配置 |
| 自动管理 | 定期创建和清理 |
下一步,让我们学习数据放置!
最后更新:2026-03-27