Skip to content

PostgreSQL 索引维护规范

核心概念

索引维护是数据库性能优化的重要组成部分,涉及以下几个关键概念:

  • 索引膨胀:由于频繁的更新和删除操作,索引页中出现大量空洞,导致索引体积增大、查询性能下降
  • 索引碎片:索引页中的数据分布不均匀,导致查询时需要读取更多的索引页
  • 索引有效性:评估索引是否被有效使用,避免维护无用索引
  • 索引重建:通过重新创建索引来消除膨胀和碎片,提高索引性能
  • 索引监控:实时监控索引的使用情况、膨胀率和性能指标

索引监控

1. 索引使用情况监控

作用:了解哪些索引被频繁使用,哪些索引是冗余的

监控方法

sql
-- 查看索引使用统计信息
SELECT 
    schemaname,
    relname AS table_name,
    indexrelname AS index_name,
    idx_scan AS index_scans,
    idx_tup_read AS tuples_read,
    idx_tup_fetch AS tuples_fetched
FROM pg_stat_user_indexes 
ORDER BY idx_scan DESC;

-- 查看未使用的索引
SELECT 
    schemaname,
    relname AS table_name,
    indexrelname AS index_name
FROM pg_stat_user_indexes 
WHERE idx_scan = 0 
AND schemaname NOT IN ('pg_catalog', 'information_schema');

2. 索引膨胀监控

作用:检测索引膨胀情况,及时进行维护

监控方法

sql
-- 安装pgstattuple扩展(如果未安装)
CREATE EXTENSION IF NOT EXISTS pgstattuple;

-- 检查单个索引的膨胀情况
SELECT 
    strelid::regclass AS table_name,
    stindexid::regclass AS index_name,
    reltuples AS estimated_rows,
    sttupcount AS actual_rows,
    (100 * (stpages - ceil(estimated_pages)) / stpages)::numeric(5,2) AS bloat_percentage
FROM (
    SELECT 
        strelid,
        stindexid,
        stpages,
        sttupcount,
        reltuples,
        ceil(reltuples / (8192 / (4 * (ceil(current_setting('block_size')::numeric / 8))))) AS estimated_pages
    FROM pg_stat_user_indexes i
    JOIN pg_index idx ON i.indexrelid = idx.indexrelid
    JOIN pg_class c ON idx.indrelid = c.oid
    CROSS JOIN LATERAL pgstattuple_approx(i.indexrelid) 
    WHERE NOT idx.indisunique
) subquery
WHERE bloat_percentage > 10
ORDER BY bloat_percentage DESC;

3. 索引大小监控

作用:了解索引的存储空间占用情况

监控方法

sql
-- 查看索引大小(按大小排序)
SELECT 
    schemaname,
    relname AS table_name,
    indexrelname AS index_name,
    pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,
    pg_size_pretty(pg_relation_size(relid)) AS table_size,
    (pg_relation_size(indexrelid)::numeric / pg_relation_size(relid)::numeric * 100)::numeric(5,2) AS index_to_table_ratio
FROM pg_stat_user_indexes
ORDER BY pg_relation_size(indexrelid) DESC;

索引维护操作

1. 索引重建

作用:消除索引膨胀和碎片,提高索引性能

适用场景

  • 索引膨胀率超过20%
  • 索引查询性能明显下降
  • 索引扫描次数频繁但性能不佳

重建方法

sql
-- 方法1:使用REINDEX命令重建单个索引
REINDEX INDEX public.users_email_idx;

-- 方法2:重建表的所有索引
REINDEX TABLE public.users;

-- 方法3:使用CONCURRENTLY选项在线重建索引(不阻塞写操作)
REINDEX INDEX CONCURRENTLY public.users_email_idx;

-- 方法4:重建整个数据库的所有索引(谨慎使用)
REINDEX DATABASE postgres;

注意事项

  • 普通REINDEX会锁定表,阻塞写操作
  • CONCURRENTLY选项不会阻塞写操作,但执行时间更长
  • 重建索引期间会消耗额外的CPU和I/O资源
  • 建议在业务低峰期执行索引重建

2. 索引收缩

作用:通过VACUUM操作回收索引中的空闲空间

适用场景

  • 索引膨胀率较低(<20%)
  • 不希望长时间锁定表

操作方法

sql
-- 对表进行VACUUM FULL操作(会锁定表)
VACUUM FULL public.users;

-- 对表进行VACUUM ANALYZE操作(不锁定表)
VACUUM ANALYZE public.users;

-- 对表进行AUTOVACUUM强制触发
ALTER TABLE public.users SET (autovacuum_enabled = true, toast.autovacuum_enabled = true);

3. 冗余索引清理

作用:删除未使用或冗余的索引,减少维护开销

识别方法

sql
-- 查找可能冗余的索引(相同列、相同顺序)
SELECT 
    schemaname,
    relname AS table_name,
    array_agg(indexrelname ORDER BY indexrelname) AS redundant_indexes,
    indkey,
    amname
FROM (
    SELECT 
        i.schemaname,
        i.relname,
        i.indexrelname,
        i.indkey,
        am.amname
    FROM pg_stat_user_indexes i
    JOIN pg_index idx ON i.indexrelid = idx.indexrelid
    JOIN pg_am am ON idx.indam = am.oid
    WHERE NOT idx.indisunique
    AND NOT idx.indisprimary
) subquery
GROUP BY schemaname, relname, indkey, amname
HAVING count(*) > 1;

删除方法

sql
-- 删除未使用的冗余索引
DROP INDEX IF EXISTS public.redundant_index_name;

索引维护策略

1. 定期维护计划

维护操作频率建议执行时间监控指标
索引使用情况检查每周业务低峰期未使用索引数量
索引膨胀检测每周业务低峰期膨胀率 > 20%
索引大小监控每月业务低峰期索引大小增长趋势
索引重建按需业务低峰期膨胀率 > 20% 或性能下降
冗余索引清理每月业务低峰期冗余索引数量

2. 自动维护配置

配置PostgreSQL自动维护参数

sql
-- 调整autovacuum相关参数
ALTER SYSTEM SET autovacuum = on;
ALTER SYSTEM SET autovacuum_max_workers = 3;
ALTER SYSTEM SET autovacuum_naptime = '1min';
ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.05;
ALTER SYSTEM SET autovacuum_analyze_scale_factor = 0.02;

-- 应用配置
SELECT pg_reload_conf();

针对特定表调整autovacuum参数

sql
-- 对频繁更新的表调整autovacuum参数
ALTER TABLE public.users 
SET (
    autovacuum_enabled = true,
    autovacuum_vacuum_scale_factor = 0.02,
    autovacuum_analyze_scale_factor = 0.01,
    autovacuum_vacuum_threshold = 50,
    autovacuum_analyze_threshold = 50
);

最佳实践

1. 生产环境维护建议

  1. 优先使用在线重建:对于生产环境的核心表,使用REINDEX CONCURRENTLY避免阻塞业务
  2. 分批次执行:将索引重建任务分批次执行,避免同时占用过多系统资源
  3. 监控重建过程:在执行索引重建时,监控系统的CPU、I/O和内存使用情况
  4. 建立回滚计划:准备好回滚方案,以防重建过程中出现意外情况
  5. 测试环境验证:在测试环境中验证索引重建的效果和影响
  6. 文档记录:详细记录索引维护操作的时间、对象、方法和结果

2. 常见问题处理

  • 问题1:REINDEX CONCURRENTLY执行失败 解决方法:检查是否有长时间运行的事务,等待事务结束后重试;或使用普通REINDEX在维护窗口执行

  • 问题2:索引重建后性能没有明显提升 解决方法:分析查询计划,检查是否有其他性能瓶颈;验证索引统计信息是否准确

  • 问题3:autovacuum没有及时清理索引膨胀 解决方法:调整autovacuum参数,或手动执行VACUUM ANALYZE操作

常见问题(FAQ)

Q1:如何判断索引是否需要重建?

A1:参考以下指标:

  • 索引膨胀率超过20%
  • 索引扫描次数频繁但查询性能下降
  • 索引大小远大于表数据大小
  • 执行计划显示索引扫描成本过高

Q2:REINDEX和VACUUM FULL有什么区别?

A2:区别如下:

  • REINDEX:重建整个索引,彻底消除膨胀和碎片
  • VACUUM FULL:重写整个表和索引,回收所有空闲空间,但会锁定表
  • REINDEX CONCURRENTLY:在线重建索引,不锁定表,但执行时间更长

Q3:如何安全地删除冗余索引?

A3:删除冗余索引的步骤:

  1. 确认索引确实未被使用(通过pg_stat_user_indexes检查)
  2. 确认索引是冗余的(具有相同的列和顺序)
  3. 在测试环境中验证删除后对性能的影响
  4. 在业务低峰期执行删除操作
  5. 删除后监控系统性能

Q4:自动维护和手动维护哪个更好?

A4:建议结合使用:

  • 自动维护:处理日常的小量膨胀和统计信息更新
  • 手动维护:处理严重的索引膨胀、冗余索引清理和性能优化
  • 定期检查:验证自动维护的效果,及时发现问题

Q5:索引维护会影响数据库性能吗?

A5:会有一定影响:

  • 索引重建会消耗CPU、I/O和内存资源
  • 普通REINDEX会锁定表,阻塞写操作
  • CONCURRENTLY选项影响较小,但执行时间更长
  • 建议在业务低峰期执行索引维护操作