Skip to content

PostgreSQL 性能优化扩展

概述

PostgreSQL 提供了丰富的内置性能优化功能,但在实际生产环境中,有时需要更强大的工具来满足复杂的性能优化需求。PostgreSQL 的扩展机制允许用户安装和使用各种性能优化扩展,以增强数据库的性能优化能力。

本文将介绍 PostgreSQL 中常用的性能优化扩展,包括它们的安装、配置、使用方法和最佳实践,帮助 DBA 和开发人员在实际工作中合理选择和使用这些扩展,提高数据库性能优化的效率和质量。

常用性能优化扩展

pg_repack - 在线表重构

什么是 pg_repack

pg_repack 是一个用于在线表重构的扩展,可以在不阻塞读写操作的情况下,重建表和索引,解决表膨胀问题,提高查询性能。

版本支持

  • 兼容 PostgreSQL 9.4+ 版本
  • PostgreSQL 14+ 支持并行重构
  • PostgreSQL 15+ 支持更多表类型

安装方法

bash
# Ubuntu/Debian
apt-get install postgresql-14-repack

# CentOS/RHEL 8+
dnf install postgresql14-repack

# 源码编译(适用于所有版本)
git clone https://github.com/reorg/pg_repack.git
cd pg_repack
make && make install
sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS pg_repack;

生产环境核心配置

postgresql.conf 中添加以下配置(可选,用于优化 pg_repack 性能):

ini
# 并行工作进程数,建议设置为 CPU 核心数的 1/4 到 1/2
max_parallel_workers_maintenance = 4

生产环境最佳实践

表膨胀评估
sql
-- 安装 pgstattuple 扩展评估表膨胀
CREATE EXTENSION IF NOT EXISTS pgstattuple;

-- 检查表膨胀情况
SELECT 
    table_len, 
    tuple_len, 
    tuple_count, 
    dead_tuple_len, 
    dead_tuple_count, 
    round((dead_tuple_len::numeric / table_len) * 100, 2) AS dead_tuple_percent
FROM pgstattuple('your_table_name');

-- 当死元组比例超过 20% 或表大小增长过快时,考虑使用 pg_repack
在线重构操作
bash
# 重建表(解决表膨胀)
pg_repack -d your_database -t your_table --jobs 4

# 重建表和索引
pg_repack -d your_database -t your_table -x --jobs 4

# 只重建索引
pg_repack -d your_database -t your_table -o --jobs 4

# 重建指定索引
pg_repack -d your_database -i your_index --jobs 4

# 使用连接串
pg_repack "host=localhost port=5432 dbname=your_database user=postgres" -t your_table --jobs 4
自动化重构脚本
bash
#!/bin/bash
# 自动检测并重构膨胀率超过 20% 的表

DB_NAME="your_database"
THRESHOLD=20
LOG_FILE="/var/log/pg_repack.log"

# 获取膨胀率超过阈值的表
INFLATED_TABLES=$(psql -d $DB_NAME -t -c "
    SELECT schemaname || '.' || relname 
    FROM pg_stat_user_tables 
    WHERE pg_total_relation_size(relid) > 104857600  -- 只处理大于 100MB 的表
    ORDER BY pg_total_relation_size(relid) DESC
")

# 对每个膨胀表执行重构
for TABLE in $INFLATED_TABLES; do
    TABLE=$(echo $TABLE | xargs)  # 去除空格
    if [ -n "$TABLE" ]; then
        echo "$(date): 开始重构表 $TABLE" >> $LOG_FILE
        pg_repack -d $DB_NAME -t "$TABLE" --jobs 4 >> $LOG_FILE 2>&1
        if [ $? -eq 0 ]; then
            echo "$(date): 表 $TABLE 重构成功" >> $LOG_FILE
        else
            echo "$(date): 表 $TABLE 重构失败" >> $LOG_FILE
        fi
    fi
done

注意事项

  • 确保有足够的磁盘空间(至少需要表大小的 1.5 倍)
  • 在业务低峰期执行,避免影响生产业务
  • PostgreSQL 14+ 支持并行重构,使用 --jobs 参数指定并行度
  • 不支持临时表、外部表和某些特殊表类型

pg_prewarm - 数据预加载

什么是 pg_prewarm

pg_prewarm 是 PostgreSQL 提供的一个数据预加载扩展,可以将表或索引的数据预加载到 PostgreSQL 的缓冲区缓存中,减少后续查询的磁盘 I/O,提高查询性能。

版本支持

  • PostgreSQL 9.4+ 内置支持
  • PostgreSQL 13+ 新增 prefetch 模式

安装方法

sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS pg_prewarm;

生产环境应用场景

数据库启动后预加载热点数据
sql
-- 数据库重启后,预加载核心业务表
SELECT pg_prewarm('users');
SELECT pg_prewarm('orders');
SELECT pg_prewarm('products');

-- 预加载索引
SELECT pg_prewarm('idx_orders_user_id', mode => 'index');
SELECT pg_prewarm('idx_users_email', mode => 'index');
表维护后预加载
sql
-- VACUUM FULL 或 pg_repack 后预加载
VACUUM FULL VERBOSE your_table;
SELECT pg_prewarm('your_table');

-- 重建索引后预加载
REINDEX INDEX your_index;
SELECT pg_prewarm('your_index', mode => 'index');
智能预加载策略
sql
-- 根据查询频率预加载表的热点区域
-- 1. 先查看表的块数量
SELECT relname, relpages FROM pg_class WHERE relname = 'your_table';

-- 2. 预加载表的前 1000 个块(通常是热点数据)
SELECT pg_prewarm('your_table', first_block => 0, last_block => 999, mode => 'buffer');

-- 3. PostgreSQL 13+ 使用预取模式,异步加载
SELECT pg_prewarm('your_table', mode => 'prefetch');
预加载效果验证
sql
-- 预加载前查看缓存命中率
SELECT 
    sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) AS cache_hit_rate
FROM pg_statio_user_tables
WHERE relname = 'your_table';

-- 执行预加载
SELECT pg_prewarm('your_table');

-- 预加载后再次查看缓存命中率
SELECT 
    sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) AS cache_hit_rate
FROM pg_statio_user_tables
WHERE relname = 'your_table';

最佳实践

  • 只预加载频繁访问的热点表和索引
  • 在业务低峰期或数据库重启后执行
  • 结合 pg_buffercache 扩展监控缓存使用情况
  • PostgreSQL 13+ 优先使用 prefetch 模式,减少对系统的影响
  • 对于非常大的表,只预加载热点数据区域

pg_partman - 分区表管理

什么是 pg_partman

pg_partman 是一个用于分区表管理的扩展,可以自动创建和管理分区表,支持按时间、数值等多种方式分区,提高大表的查询性能。

版本支持

  • 兼容 PostgreSQL 9.6+ 版本
  • PostgreSQL 10+ 支持原生分区表
  • PostgreSQL 13+ 支持并行分区维护

安装方法

bash
# Ubuntu/Debian
apt-get install postgresql-14-partman

# CentOS/RHEL 8+
dnf install postgresql14-partman
sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS pg_partman;

生产环境配置

postgresql.conf 中添加以下配置:

ini
# 启用 pg_partman 自动维护
shared_preload_libraries = 'pg_partman_bgw'
pg_partman_bgw.interval = 3600  # 自动维护间隔(秒)
pg_partman_bgw.role = 'postgres'  # 执行维护的角色

生产环境最佳实践

按时间分区表创建
sql
-- 创建父表
CREATE TABLE logs (
    id SERIAL, 
    event_time TIMESTAMP NOT NULL, 
    event_type VARCHAR(50),
    event_data JSONB
);

-- 初始化分区(PostgreSQL 10+ 原生分区)
SELECT partman.create_parent(
    p_parent_table => 'public.logs',
    p_control => 'event_time',
    p_type => 'native',
    p_interval => 'daily',
    p_premake => 7,  -- 预创建 7 天的分区
    p_start_partition => '2023-01-01',  -- 开始分区日期
    p_inherit_fk => true  -- 继承外键约束
);

-- 查看分区情况
SELECT * FROM partman.show_partitions('public.logs');
分区自动维护
sql
-- 手动触发分区维护
SELECT partman.run_maintenance_proc();

-- 结合 pg_cron 定期执行
SELECT cron.schedule(
    'hourly-partman-maintenance',
    '0 * * * *',
    'CALL partman.run_maintenance_proc()'
);

-- 配置分区保留策略(保留 30 天数据)
UPDATE partman.part_config 
SET retention = '30 days', 
    retention_keep_table = false,  -- 自动删除过期分区
    retention_keep_index = false
WHERE parent_table = 'public.logs';
分区表性能优化
sql
-- 确保查询包含分区键
EXPLAIN ANALYZE 
SELECT * FROM logs 
WHERE event_time BETWEEN '2023-01-01' AND '2023-01-31';  -- 只扫描相关分区

-- 避免全部分区扫描
EXPLAIN ANALYZE 
SELECT * FROM logs 
WHERE event_type = 'error';  -- 可能扫描所有分区

-- 为分区表添加合适的索引
CREATE INDEX idx_logs_event_type ON logs(event_type) INCLUDE (event_data);

注意事项

  • 选择合适的分区间隔,避免分区过多或过少
  • 确保查询包含分区键,充分利用分区裁剪
  • 定期监控分区大小和数量,调整保留策略
  • PostgreSQL 13+ 支持并行分区维护,提高维护效率

pg_hint_plan - 查询计划控制

什么是 pg_hint_plan

pg_hint_plan 是一个用于控制 PostgreSQL 查询计划的扩展,可以通过注释的方式向查询优化器提供提示,强制使用特定的索引或连接方式,提高查询性能。

版本支持

  • 兼容 PostgreSQL 9.5+ 版本
  • PostgreSQL 13+ 支持更多提示类型
  • PostgreSQL 15+ 支持 JSON 格式提示

安装方法

bash
# Ubuntu/Debian(来自 pgapt 仓库)
apt-get install postgresql-14-pg-hint-plan

# 源码编译
wget https://github.com/ossc-db/pg_hint_plan/archive/refs/tags/REL14_1_4_0.tar.gz
tar -xzf REL14_1_4_0.tar.gz
cd pg_hint_plan-REL14_1_4_0
make && make install
sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS pg_hint_plan;

配置参数

postgresql.conf 中添加以下配置:

ini
# 启用 pg_hint_plan 扩展
shared_preload_libraries = 'pg_hint_plan'

# 配置参数
pg_hint_plan.enable_hint = on
pg_hint_plan.debug_print = off  # 生产环境关闭调试输出
pg_hint_plan.parse_messages = warning

生产环境最佳实践

基本索引提示
sql
-- 强制使用指定索引
/*+ IndexScan(users idx_users_email) */
SELECT * FROM users WHERE email = 'user@example.com';

-- 强制使用位图索引扫描
/*+ BitmapScan(orders idx_orders_status) */
SELECT * FROM orders WHERE status = 'completed';
连接顺序和方法提示
sql
-- 强制连接顺序和连接方法
/*+ 
    Leading(users orders) 
    HashJoin(users orders) 
    IndexScan(users idx_users_id) 
    IndexScan(orders idx_orders_user_id) 
*/
SELECT u.*, o.* 
FROM users u 
JOIN orders o ON u.id = o.user_id 
WHERE u.status = 'active';
并行查询提示
sql
-- 强制使用并行查询
/*+ Parallel(orders 4) */
SELECT count(*) FROM orders WHERE order_date > '2023-01-01';

-- 禁用并行查询(某些情况下并行查询可能更慢)
/*+ NoParallel(complex_query_view) */
SELECT * FROM complex_query_view;
慎用场景和注意事项
  • 只在查询计划明显错误时使用
  • 定期验证提示的有效性,特别是在版本升级后
  • 避免过度依赖提示,优先优化 schema 和统计信息
  • 记录使用提示的原因和测试结果
  • 结合 pg_stat_statements 监控使用提示的查询性能

hypopg - 虚拟索引

什么是 hypopg

hypopg 是一个用于创建虚拟索引的扩展,可以在不实际创建索引的情况下,测试索引对查询性能的影响,帮助 DBA 选择合适的索引策略。

版本支持

  • 兼容 PostgreSQL 9.5+ 版本
  • PostgreSQL 14+ 支持并行查询测试

安装方法

sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS hypopg;

生产环境应用

测试索引效果
sql
-- 1. 先查看当前查询计划
EXPLAIN ANALYZE 
SELECT * FROM users WHERE email = 'user@example.com';

-- 2. 创建虚拟索引
SELECT hypopg_create_index('CREATE INDEX ON users (email)');

-- 3. 再次查看查询计划,检查是否使用虚拟索引
EXPLAIN ANALYZE 
SELECT * FROM users WHERE email = 'user@example.com';

-- 4. 查看虚拟索引列表
SELECT * FROM hypopg_list_indexes();

-- 5. 删除虚拟索引
SELECT hypopg_drop_index(indexrelid) FROM hypopg_list_indexes() WHERE indexname LIKE '%users_email%';
测试多个索引组合
sql
-- 测试复合索引效果
SELECT hypopg_create_index('CREATE INDEX ON orders (user_id, order_date DESC)');

-- 测试部分索引效果
SELECT hypopg_create_index('CREATE INDEX ON orders (order_date) WHERE status = ''completed''');

-- 比较不同索引的查询成本
EXPLAIN ANALYZE 
SELECT * FROM orders WHERE user_id = 123 AND order_date > '2023-01-01';

-- 清理所有虚拟索引
SELECT hypopg_reset();
索引策略评估
sql
-- 结合 pg_stat_statements 识别需要优化的查询
SELECT queryid, query, calls, total_time 
FROM pg_stat_statements 
ORDER BY total_time DESC 
LIMIT 5;

-- 对慢查询创建虚拟索引并测试
-- 例如,对以下查询测试索引效果
/*
SELECT * FROM orders WHERE user_id = $1 AND status = $2 ORDER BY order_date DESC;
*/

-- 测试不同索引方案
SELECT hypopg_create_index('CREATE INDEX ON orders (user_id, status, order_date DESC)');
SELECT hypopg_create_index('CREATE INDEX ON orders (status, user_id, order_date DESC)');

-- 比较查询计划成本
EXPLAIN 
SELECT * FROM orders WHERE user_id = 123 AND status = 'completed' ORDER BY order_date DESC;

最佳实践

  • 只在测试环境或生产环境低峰期使用
  • 避免创建过多虚拟索引,影响查询计划生成
  • 结合 EXPLAIN ANALYZE 进行真实性能测试
  • 虚拟索引只对当前会话有效,会话结束后自动清理
  • 用于辅助决策,最终以实际创建索引后的性能为准

pg_squeeze - 表压缩工具

什么是 pg_squeeze

pg_squeeze 是一个用于表压缩的扩展,可以在不阻塞读写操作的情况下,压缩表和索引,减少存储空间占用,提高查询性能。

版本支持

  • 兼容 PostgreSQL 10+ 版本
  • PostgreSQL 12+ 支持并行压缩
  • PostgreSQL 14+ 支持更多压缩算法

安装方法

bash
# 源码编译安装
git clone https://github.com/cybertec-postgresql/pg_squeeze.git
cd pg_squeeze
make && make install
sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS pg_squeeze;

配置参数

postgresql.conf 中添加以下配置:

ini
# 启用 pg_squeeze 扩展
shared_preload_libraries = 'pg_squeeze'

# 压缩任务工作进程数
pg_squeeze.max_worker_processes = 4

# 自动压缩开关
pg_squeeze.auto = on

生产环境使用

手动压缩表
sql
-- 压缩表,设置填充因子为 80
SELECT squeeze.squeeze_table(
    p_table := 'public.your_table',
    p_index := 'your_table_pkey',
    p_fillfactor := 80
);

-- 查看压缩任务状态
SELECT * FROM squeeze.job;

-- 查看压缩历史
SELECT * FROM squeeze.history ORDER BY started_at DESC;
自动压缩配置
sql
-- 配置表自动压缩策略
INSERT INTO squeeze.tables (
    tableoid, 
    enabled, 
    fillfactor, 
    autocompression_enabled
) VALUES (
    'public.your_table'::regclass,
    true,
    80,
    true
);

-- 设置自动压缩阈值(当表膨胀超过 30% 时自动压缩)
UPDATE squeeze.tables 
SET autocompression_pct = 30 
WHERE tableoid = 'public.your_table'::regclass;
压缩效果验证
sql
-- 压缩前查看表大小
SELECT pg_size_pretty(pg_total_relation_size('your_table')) AS before_compression;

-- 执行压缩
SELECT squeeze.squeeze_table(p_table := 'public.your_table');

-- 压缩后查看表大小
SELECT pg_size_pretty(pg_total_relation_size('your_table')) AS after_compression;

性能优化扩展选择指南

按场景选择扩展

优化场景推荐扩展版本要求性能影响
表膨胀处理pg_repack9.4+低(在线操作)
缓存优化pg_prewarm9.4+极低
大表性能pg_partman9.6+极低
查询计划调优pg_hint_plan9.5+
索引策略测试hypopg9.5+极低
存储空间优化pg_squeeze10+

生产环境部署建议

  1. 基础优化组合

    • pg_repack:定期处理表膨胀
    • pg_prewarm:数据库重启后预加载热点数据
    • hypopg:测试索引策略
  2. 中大型数据库

    • 增加 pg_partman:管理分区表
    • 增加 pg_squeeze:压缩表空间
  3. 复杂查询优化

    • 增加 pg_hint_plan:调优特定查询计划

性能影响评估

扩展名称CPU 影响I/O 影响内存影响锁影响
pg_repack无(在线操作)
pg_prewarm
pg_partman极低极低极低
pg_hint_plan
hypopg极低极低
pg_squeeze无(在线操作)

扩展维护最佳实践

定期更新扩展

sql
-- 查看已安装扩展版本
SELECT extname, extversion FROM pg_extension;

-- 更新扩展到最新版本
ALTER EXTENSION pg_repack UPDATE;
ALTER EXTENSION pg_partman UPDATE;

监控扩展性能

sql
-- 监控 pg_repack 进程
SELECT * FROM pg_stat_activity WHERE query LIKE '%pg_repack%';

-- 监控 pg_partman 维护作业
SELECT * FROM pg_stat_activity WHERE query LIKE '%partman%';

-- 查看扩展占用的资源
SELECT * FROM pg_stat_user_functions WHERE funcname LIKE '%pg_repack%' OR funcname LIKE '%partman%';

版本升级注意事项

  1. 升级前

    • 检查所有扩展的版本兼容性
    • 备份数据库和扩展相关元数据
    • 在测试环境验证升级
  2. 升级后

    • 重新编译或安装扩展
    • 更新扩展版本
    • 测试扩展功能正常
    • 验证依赖关系

总结

PostgreSQL 的性能优化扩展提供了丰富的功能,可以帮助 DBA 解决各种复杂的性能问题。选择合适的扩展组合,结合最佳实践进行配置和使用,可以显著提高数据库的性能和稳定性。

在实际生产环境中,建议:

  1. 从基础扩展开始,逐步引入高级扩展
  2. 定期评估扩展的使用效果和性能影响
  3. 结合监控工具,持续优化扩展配置
  4. 关注扩展的版本兼容性,及时更新
  5. 建立完善的扩展变更管理流程

通过合理使用这些性能优化扩展,可以有效解决 PostgreSQL 在实际生产环境中遇到的各种性能挑战,提高数据库的整体性能和可靠性。