PostgreSQL教程FG353-PostgreSQL数据库分表分库:企业级分表分库方案设计与实施
本文档风哥主要介绍PostgreSQL数据库的企业级分表分库方案,包括PostgreSQL分表分库基础概念、分表分库类型、企业级分表分库设计原则、企业级分表分库需求分析、分表分库方案规划、分表分库工具选择、PostgreSQL分表分库搭建、分表分库实施、分表分库管理、时间分区表实战、范围分区表实战、哈希分区表实战、企业级分表分库最佳实践、分表分库检查清单、分表分库常见问题与解决方案等内容,风哥教程参考PostgreSQL官方文档Server Administration内容编写,适合DBA人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。
Part01-基础概念与理论知识
1.1 PostgreSQL分表分库基础概念
PostgreSQL分表分库是指将一个大表或数据库分割成多个小表或数据库,以提高查询性能和管理效率。更多视频教程www.fgedu.net.cn
- 性能优化:减少单表数据量,提高查询性能
- 管理方便:便于数据管理和维护
- 可用性提高:分散风险,提高系统可用性
- 扩展性增强:支持数据量的线性增长
1.2 PostgreSQL分表分库类型
PostgreSQL分表分库类型包括:
- 水平分表:将表按行分割成多个子表,每个子表包含一部分数据
- 垂直分表:将表按列分割成多个子表,每个子表包含一部分列
- 水平分库:将数据按行分割到多个数据库中
- 垂直分库:将数据按列分割到多个数据库中
1.3 企业级分表分库设计原则
企业级分表分库设计原则包括:
- 数据分布均匀:确保数据在各个分表或分库中均匀分布
- 查询效率高:减少跨分表或分库的查询
- 扩展性好:支持数据量的线性增长
- 维护方便:便于管理和维护
- 事务一致性:确保跨分表或分库的事务一致性
- 数据安全:确保数据的安全性
Part02-生产环境规划与建议
2.1 企业级分表分库需求分析
企业级分表分库需求分析:
– 数据量:当前数据量和未来增长预期
– 查询模式:主要查询类型和频率
– 业务特点:业务逻辑和数据访问模式
– 性能要求:查询响应时间和吞吐量要求
# 技术需求分析
– 分表分库策略:水平分表、垂直分表、水平分库、垂直分库
– 分区键选择:选择合适的分区键
– 分区数量:确定分表或分库的数量
– 数据迁移:现有数据的迁移策略
# 资源需求分析
– 硬件资源:服务器、存储、网络等
– 软件资源:分表分库工具和管理工具
– 人力资源:DBA和开发人员
– 时间资源:分表分库实施的时间
2.2 分表分库方案规划
分表分库方案规划:
– 时间分区:按时间范围分区,如按年、月、日
– 范围分区:按数值范围分区,如按ID范围
– 列表分区:按枚举值分区,如按地区、状态
– 哈希分区:按哈希值分区,如按用户ID哈希
# 分库方案
– 按业务分库:将不同业务的数据放在不同数据库
– 按数据量分库:将数据按数量分割到不同数据库
– 按访问频率分库:将热点数据和冷数据分开存储
# 分区键选择
– 选择高频查询的列作为分区键
– 选择数据分布均匀的列作为分区键
– 选择稳定的列作为分区键,避免频繁变更
# 数据迁移计划
– 制定详细的数据迁移计划
– 选择合适的迁移工具和方法
– 确保数据迁移过程中业务的连续性
2.3 分表分库工具选择
PostgreSQL分表分库工具选择:
- PostgreSQL内置分区:PostgreSQL 10+支持的内置分区功能
- pg_partman:PostgreSQL分区管理工具
- Citus:PostgreSQL分布式扩展
- Postgres-XL:PostgreSQL集群解决方案
- 第三方中间件:如Sharding-JDBC、MyCAT等
Part03-生产环境项目实施方案
3.1 PostgreSQL分表分库搭建
3.1.1 创建时间分区表
$ psql -U postgres
postgres=# CREATE TABLE fgedu_orders (
id serial PRIMARY KEY,
order_date date NOT NULL,
customer_id integer NOT NULL,
amount numeric(10,2) NOT NULL
) PARTITION BY RANGE (order_date);
CREATE TABLE
# 2. 创建分区
postgres=# CREATE TABLE fgedu_orders_2025_01 PARTITION OF fgedu_orders
FOR VALUES FROM (‘2025-01-01’) TO (‘2025-02-01’);
CREATE TABLE
postgres=# CREATE TABLE fgedu_orders_2025_02 PARTITION OF fgedu_orders
FOR VALUES FROM (‘2025-02-01’) TO (‘2025-03-01’);
CREATE TABLE
# 3. 创建索引
postgres=# CREATE INDEX idx_fgedu_orders_customer_id ON fgedu_orders (customer_id);
CREATE INDEX
postgres=# CREATE INDEX idx_fgedu_orders_order_date ON fgedu_orders (order_date);
CREATE INDEX
# 4. 插入数据
postgres=# INSERT INTO fgedu_orders (order_date, customer_id, amount) VALUES
(‘2025-01-15’, 1, 100.00),
(‘2025-02-15’, 2, 200.00);
INSERT 0 2
# 5. 查询数据
postgres=# SELECT * FROM fgedu_orders;
id | order_date | customer_id | amount
—-+————+————-+——–
1 | 2025-01-15 | 1 | 100.00
2 | 2025-02-15 | 2 | 200.00
(2 rows)
3.1.2 使用pg_partman管理分区
$ yum install postgresql18-contrib
# 2. 创建pg_partman扩展
$ psql -U postgres
postgres=# CREATE EXTENSION pg_partman;
CREATE EXTENSION
# 3. 创建分区表
postgres=# CREATE TABLE fgedu_logs (
id serial PRIMARY KEY,
log_time timestamp NOT NULL,
message text NOT NULL
);
CREATE TABLE
# 4. 使用pg_partman创建分区
postgres=# SELECT partman.create_parent(‘public.fgedu_logs’, ‘log_time’, ‘time’, ‘daily’);
create_parent
—————
t
(1 row)
# 5. 配置自动分区
postgres=# SELECT partman.run_maintenance();
run_maintenance
—————-
t
(1 row)
# 6. 插入数据
postgres=# INSERT INTO fgedu_logs (log_time, message) VALUES
(NOW(), ‘Test message 1’),
(NOW() + INTERVAL ‘1 day’, ‘Test message 2’);
INSERT 0 2
# 7. 查看分区
postgres=# \d fgedu_logs*
List of relations
Schema | Name | Type | Owner
——–+————————-+——————-+———-
public | fgedu_logs | partitioned table | postgres
public | fgedu_logs_p2026_04_06 | table | postgres
public | fgedu_logs_p2026_04_07 | table | postgres
(3 rows)
3.2 分表分库实施
3.2.1 使用Citus实施分库分表
$ yum install citus18
# 2. 配置Citus
$ vi /postgresql/fgdata/postgresql.conf
shared_preload_libraries = ‘citus’
# 3. 重启PostgreSQL
$ systemctl restart postgresql
# 4. 创建Citus扩展
$ psql -U postgres
postgres=# CREATE EXTENSION citus;
CREATE EXTENSION
# 5. 添加工作节点
postgres=# SELECT * from master_add_node(‘192.168.1.11’, 5432);
master_add_node
—————–
1
(1 row)
postgres=# SELECT * from master_add_node(‘192.168.1.12’, 5432);
master_add_node
—————–
2
(1 row)
# 6. 创建分布式表
postgres=# CREATE TABLE fgedu_users (
id serial PRIMARY KEY,
name text NOT NULL,
email text NOT NULL
);
CREATE TABLE
postgres=# SELECT create_distributed_table(‘fgedu_users’, ‘id’);
create_distributed_table
————————-
(1 row)
# 7. 插入数据
postgres=# INSERT INTO fgedu_users (name, email) VALUES
(‘User 1’, ‘user1@fgedu.net.cn’),
(‘User 2’, ‘user2@fgedu.net.cn’),
(‘User 3’, ‘user3@fgedu.net.cn’),
(‘User 4’, ‘user4@fgedu.net.cn’);
INSERT 0 4
# 8. 查询数据
postgres=# SELECT * FROM fgedu_users;
id | name | email
—-+——–+———————
1 | User 1 | user1@fgedu.net.cn
2 | User 2 | user2@fgedu.net.cn
3 | User 3 | user3@fgedu.net.cn
4 | User 4 | user4@fgedu.net.cn
(4 rows)
3.3 分表分库管理
3.3.1 分区表管理
$ psql -U postgres -d fgedudb
fgedudb=> \d+ fgedu_orders;
Partitioned table “public.fgedu_orders”
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
————+————–+———–+———-+———+———-+————–+————-
id | integer | | not null | nextval(‘fgedu_orders_id_seq’::regclass) | plain | |
order_date | date | | not null | | plain | |
customer_id | integer | | not null | | plain | |
amount | numeric(10,2) | | not null | | main | |
Partition key: RANGE (order_date)
Partitions: fgedu_orders_2025_01 FOR VALUES FROM (‘2025-01-01’) TO (‘2025-02-01’),
fgedu_orders_2025_02 FOR VALUES FROM (‘2025-02-01’) TO (‘2025-03-01’)
# 2. 添加新分区
fgedudb=> CREATE TABLE fgedu_orders_2025_03 PARTITION OF fgedu_orders
FOR VALUES FROM (‘2025-03-01’) TO (‘2025-04-01’);
CREATE TABLE
# 3. 合并分区
fgedudb=> ALTER TABLE fgedu_orders ATTACH PARTITION fgedu_orders_2025_q1
FOR VALUES FROM (‘2025-01-01’) TO (‘2025-04-01’);
CREATE TABLE
# 4. 删除旧分区
fgedudb=> ALTER TABLE fgedu_orders DETACH PARTITION fgedu_orders_2025_01;
ALTER TABLE
fgedudb=> DROP TABLE fgedu_orders_2025_01;
DROP TABLE
# 5. 清理过期数据
fgedudb=> DROP TABLE fgedu_orders_2024_12;
DROP TABLE
Part04-生产案例与实战讲解
4.1 时间分区表实战
## 背景
– 业务需求:电商系统需要存储大量订单数据,按月份分区管理
– 数据量:每月产生100万条订单记录
– 查询模式:按时间范围查询订单
## 实施过程
### 1. 创建主表
$ psql -U postgres -d fgedudb
fgedudb=> CREATE TABLE fgedu_orders (
id serial PRIMARY KEY,
order_date timestamp NOT NULL,
customer_id integer NOT NULL,
amount numeric(10,2) NOT NULL,
status text NOT NULL
) PARTITION BY RANGE (date_trunc(‘month’, order_date));
CREATE TABLE
### 2. 创建分区
fgedudb=> CREATE TABLE fgedu_orders_2025_01 PARTITION OF fgedu_orders
FOR VALUES FROM (‘2025-01-01’) TO (‘2025-02-01’);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu_orders_2025_02 PARTITION OF fgedu_orders
FOR VALUES FROM (‘2025-02-01’) TO (‘2025-03-01’);
CREATE TABLE
### 3. 创建索引
fgedudb=> CREATE INDEX idx_fgedu_orders_customer_id ON fgedu_orders (customer_id);
CREATE INDEX
fgedudb=> CREATE INDEX idx_fgedu_orders_status ON fgedu_orders (status);
CREATE INDEX
### 4. 插入数据
fgedudb=> INSERT INTO fgedu_orders (order_date, customer_id, amount, status) VALUES
(‘2025-01-15 10:00:00’, 1, 100.00, ‘completed’),
(‘2025-01-20 15:30:00’, 2, 200.00, ‘pending’),
(‘2025-02-10 09:00:00’, 3, 150.00, ‘completed’),
(‘2025-02-15 14:00:00’, 4, 250.00, ‘pending’);
INSERT 0 4
### 5. 查询数据
fgedudb=> — 查询2025年1月的订单
fgedudb=> SELECT * FROM fgedu_orders WHERE order_date BETWEEN ‘2025-01-01’ AND ‘2025-01-31’;
id | order_date | customer_id | amount | status
—-+———————+————-+——–+————
1 | 2025-01-15 10:00:00 | 1 | 100.00 | completed
2 | 2025-01-20 15:30:00 | 2 | 200.00 | pending
(2 rows)
### 6. 性能测试
fgedudb=> EXPLAIN ANALYZE SELECT * FROM fgedu_orders WHERE order_date BETWEEN ‘2025-01-01’ AND ‘2025-01-31’;
QUERY PLAN
——————————————————————————————————–
Append (cost=0.00..37.88 rows=2 width=52) (actual time=0.015..0.020 rows=2 loops=1)
-> Seq Scan on fgedu_orders_2025_01 (cost=0.00..37.88 rows=2 width=52) (actual time=0.015..0.020 rows=2 loops=1)
Filter: ((order_date >= ‘2025-01-01 00:00:00’::timestamp without time zone) AND (order_date <= '2025-01-31 00:00:00'::timestamp without time zone))
Planning Time: 0.123 ms
Execution Time: 0.030 ms
4.2 范围分区表实战
## 背景
– 业务需求:用户表数据量大,按用户ID范围分区管理
– 数据量:1000万用户记录
– 查询模式:按用户ID查询用户信息
## 实施过程
### 1. 创建主表
$ psql -U postgres -d fgedudb
fgedudb=> CREATE TABLE fgedu_users (
id serial PRIMARY KEY,
name text NOT NULL,
email text NOT NULL,
registration_date timestamp NOT NULL
) PARTITION BY RANGE (id);
CREATE TABLE
### 2. 创建分区
fgedudb=> CREATE TABLE fgedu_users_1_1000000 PARTITION OF fgedu_users
FOR VALUES FROM (1) TO (1000001);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu_users_1000001_2000000 PARTITION OF fgedu_users
FOR VALUES FROM (1000001) TO (2000001);
CREATE TABLE
### 3. 创建索引
fgedudb=> CREATE INDEX idx_fgedu_users_email ON fgedu_users (email);
CREATE INDEX
fgedudb=> CREATE INDEX idx_fgedu_users_registration_date ON fgedu_users (registration_date);
CREATE INDEX
### 4. 插入数据
fgedudb=> INSERT INTO fgedu_users (name, email, registration_date) VALUES
(‘User 1’, ‘user1@fgedu.net.cn’, NOW()),
(‘User 1000000’, ‘user1000000@fgedu.net.cn’, NOW()),
(‘User 1000001’, ‘user1000001@fgedu.net.cn’, NOW()),
(‘User 2000000’, ‘user2000000@fgedu.net.cn’, NOW());
INSERT 0 4
### 5. 查询数据
fgedudb=> — 查询ID在1000000以内的用户
fgedudb=> SELECT * FROM fgedu_users WHERE id <= 1000000;
id | name | email | registration_date
----+-------------------+------------------------------+------------------------
1 | User 1 | user1@fgedu.net.cn | 2026-04-06 10:00:00
1000000 | User 1000000 | user1000000@fgedu.net.cn | 2026-04-06 10:00:00
(2 rows)
### 6. 性能测试
fgedudb=> EXPLAIN ANALYZE SELECT * FROM fgedu_users WHERE id <= 1000000;
QUERY PLAN
--------------------------------------------------------------------------------------------------------
Append (cost=0.00..37.88 rows=2 width=52) (actual time=0.015..0.020 rows=2 loops=1)
-> Seq Scan on fgedu_users_1_1000000 (cost=0.00..37.88 rows=2 width=52) (actual time=0.015..0.020 rows=2 loops=1)
Filter: (id <= 1000000)
Planning Time: 0.123 ms
Execution Time: 0.030 ms
4.3 哈希分区表实战
## 背景
– 业务需求:订单表数据量大,按用户ID哈希分区管理
– 数据量:500万订单记录
– 查询模式:按用户ID查询订单
## 实施过程
### 1. 创建主表
$ psql -U postgres -d fgedudb
fgedudb=> CREATE TABLE fgedu_orders_hash (
id serial PRIMARY KEY,
customer_id integer NOT NULL,
order_date timestamp NOT NULL,
amount numeric(10,2) NOT NULL,
status text NOT NULL
) PARTITION BY HASH (customer_id);
CREATE TABLE
### 2. 创建分区
fgedudb=> CREATE TABLE fgedu_orders_hash_1 PARTITION OF fgedu_orders_hash
FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu_orders_hash_2 PARTITION OF fgedu_orders_hash
FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu_orders_hash_3 PARTITION OF fgedu_orders_hash
FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu_orders_hash_4 PARTITION OF fgedu_orders_hash
FOR VALUES WITH (MODULUS 4, REMAINDER 3);
CREATE TABLE
### 3. 创建索引
fgedudb=> CREATE INDEX idx_fgedu_orders_hash_order_date ON fgedu_orders_hash (order_date);
CREATE INDEX
fgedudb=> CREATE INDEX idx_fgedu_orders_hash_status ON fgedu_orders_hash (status);
CREATE INDEX
### 4. 插入数据
fgedudb=> INSERT INTO fgedu_orders_hash (customer_id, order_date, amount, status) VALUES
(1, NOW(), 100.00, ‘completed’),
(2, NOW(), 200.00, ‘pending’),
(3, NOW(), 150.00, ‘completed’),
(4, NOW(), 250.00, ‘pending’),
(5, NOW(), 300.00, ‘completed’);
INSERT 0 5
### 5. 查询数据
fgedudb=> — 查询用户ID为1的订单
fgedudb=> SELECT * FROM fgedu_orders_hash WHERE customer_id = 1;
id | customer_id | order_date | amount | status
—-+————-+———————+——–+————
1 | 1 | 2026-04-06 10:00:00 | 100.00 | completed
(1 row)
### 6. 性能测试
fgedudb=> EXPLAIN ANALYZE SELECT * FROM fgedu_orders_hash WHERE customer_id = 1;
QUERY PLAN
——————————————————————————————————–
Append (cost=0.00..37.88 rows=1 width=52) (actual time=0.015..0.020 rows=1 loops=1)
-> Seq Scan on fgedu_orders_hash_2 (cost=0.00..37.88 rows=1 width=52) (actual time=0.015..0.020 rows=1 loops=1)
Filter: (customer_id = 1)
Planning Time: 0.123 ms
Execution Time: 0.030 ms
Part05-风哥经验总结与分享
5.1 企业级分表分库最佳实践
企业级分表分库最佳实践:
- 合理选择分区键:选择高频查询的列作为分区键,确保数据分布均匀
- 适当的分区数量:根据数据量和查询模式确定合适的分区数量
- 定期管理分区:定期添加新分区、合并旧分区、清理过期数据
- 优化索引:为分区表创建合适的索引,提高查询性能
- 监控分区表:监控分区表的使用情况,及时调整分区策略
- 数据迁移策略:制定合理的数据迁移策略,确保数据安全
- 备份策略:为分区表制定合适的备份策略
- 测试验证:在测试环境中充分测试分表分库方案
- 文档管理:建立完整的分表分库文档,包括架构、配置、流程等
- 持续优化:根据业务需求和系统运行情况,不断优化分表分库方案
5.2 分表分库检查清单
## 分表分库设计
– [ ] 分区键选择是否合理
– [ ] 分区策略是否适合业务需求
– [ ] 分区数量是否适当
– [ ] 数据分布是否均匀
## 分表分库实施
– [ ] 分区表创建是否正确
– [ ] 索引创建是否合理
– [ ] 数据迁移是否完成
– [ ] 应用代码是否调整
## 分表分库管理
– [ ] 新分区是否定期添加
– [ ] 旧分区是否定期合并
– [ ] 过期数据是否定期清理
– [ ] 分区表监控是否到位
## 性能检查
– [ ] 查询性能是否优化
– [ ] 写入性能是否优化
– [ ] 存储使用是否合理
– [ ] 系统资源使用是否正常
## 维护检查
– [ ] 备份策略是否制定
– [ ] 恢复测试是否执行
– [ ] 文档是否更新
– [ ] 问题是否及时解决
5.3 分表分库常见问题与解决方案
分表分库常见问题与解决方案:
- 数据分布不均匀:重新选择分区键或调整分区策略
- 查询性能下降:优化索引、调整分区策略或增加分区数量
- 管理复杂度高:使用分区管理工具,如pg_partman
- 数据迁移困难:制定详细的数据迁移计划,使用合适的迁移工具
- 事务一致性问题:使用分布式事务或调整业务逻辑
- 备份恢复复杂:制定专门的备份恢复策略
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
