Add design, review, and production-readiness documents for the April remediation cycle.\nInclude supporting SQL and supply-api operational design notes so review conclusions and implementation guidance stay versioned together.
7.5 KiB
7.5 KiB
数据库索引维护策略 v1.0
文档版本: v1.0 创建日期: 2026-04-07 问题: P1-009 高频写入表的索引维护策略未定义
1. 概述
本文档定义高频写入表的索引维护策略,包括 REINDEX、VACUUM 自动化方案,确保数据库性能稳定。
1.1 高频写入表清单
| 表名 | 写入频率 | 日均增量 | 备注 |
|---|---|---|---|
| supply_usage_records | 极高 | ~1000万条 | 核心业务表 |
| supply_idempotency_records | 高 | ~100万条 | 幂等检查 |
| audit_events | 高 | ~500万条 | 审计日志 |
| billing_ledger_entries | 中 | ~10万条 | 账务明细 |
2. VACUUM 维护策略
2.1 自动 VACUUM 配置
PostgreSQL 默认启用 autovacuum,但需要针对高频表进行调优:
-- supply_usage_records 表配置
ALTER TABLE supply_usage_records SET (
autovacuum_vacuum_threshold = 50,
autovacuum_analyze_threshold = 50,
autovacuum_vacuum_scale_factor = 0.01,
autovacuum_analyze_scale_factor = 0.01,
autovacuum_vacuum_cost_delay = 2,
autovacuum_vacuum_cost_limit = 200
);
-- supply_idempotency_records 表配置
ALTER TABLE supply_idempotency_records SET (
autovacuum_vacuum_threshold = 100,
autovacuum_analyze_threshold = 100,
autovacuum_vacuum_scale_factor = 0.05,
autovacuum_analyze_scale_factor = 0.02
);
2.2 VACUUM 策略矩阵
| 表名 | autovacuum_enabled | vacuum_threshold | vacuum_scale_factor | 分析频率 |
|---|---|---|---|---|
| supply_usage_records | true | 50 | 0.01 (1%) | 每1%变化 |
| supply_idempotency_records | true | 100 | 0.05 (5%) | 每2%变化 |
| supply_orders | true | 500 | 0.05 (5%) | 每周 |
| supply_packages | true | 1000 | 0.1 (10%) | 每月 |
2.3 手动 VACUUM 计划
日常维护 (低峰期 02:00-04:00):
# vacuum analyze 高频表
vacuumdb -h localhost -U postgres -d supply_db \
--table 'supply_usage_records' \
--analyze \
--verbose
# 批量 vacuum 多个表
vacuumdb -h localhost -U postgres -d supply_db \
--all \
--analyze \
--verbose
周维护 (周日 03:00-05:00):
# 全面 vacuum + analyze
vacuumdb -h localhost -U postgres -d supply_db \
--all \
--analyze \
--full \
--verbose
3. REINDEX 维护策略
3.1 REINDEX 触发条件
| 触发条件 | 说明 | 影响 |
|---|---|---|
| 索引膨胀率 > 20% | B-tree 索引膨胀 | 性能下降 |
| 大量删除后 | DELETE > 30% 总行数 | 索引包含大量空页 |
| 长时间运行后 | 运行 > 30天 | 索引统计信息陈旧 |
| 硬件故障后 | 系统重启 | 确保索引一致性 |
3.2 索引膨胀检测
-- 检测索引膨胀率
SELECT
schemaname,
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,
idx_scan,
idx_tup_read,
idx_tup_fetch,
ROUND(
(pg_relation_size(indexrelid)::numeric /
pg_relation_size(indrelid) * 100),
2
) AS index_ratio
FROM
pg_stat_user_indexes
WHERE
pg_relation_size(indexrelid) > 1024 * 1024 -- > 1MB
ORDER BY
pg_relation_size(indexrelid) DESC;
3.3 REINDEX 执行计划
月维护 (每月第一个周日 04:00-06:00):
# 重建单个膨胀索引
reindexdb -h localhost -U postgres -d supply_db \
--index 'idx_supply_usage_records_request_id' \
--verbose
# 重建表的所有索引
reindexdb -h localhost -U postgres -d supply_db \
--table 'supply_usage_records' \
--verbose
# 全库索引重建 (慎用,会锁表)
reindexdb -h localhost -U postgres -d supply_db \
--all \
--verbose
3.4 联机型 REINDEX 方案
对于不可停机的关键表,使用 REINDEX CONCURRENTLY:
# 联机重建索引 (不锁表)
reindexdb -h localhost -U postgres -d supply_db \
--index 'idx_supply_usage_records_request_id' \
--concurrently \
--verbose
4. 自动化脚本
4.1 每日维护脚本 (daily_vacuum.sh)
#!/bin/bash
# daily_vacuum.sh - 每日索引维护
# 执行时间: 每日 02:00
set -e
DB_HOST="localhost"
DB_PORT="5432"
DB_NAME="supply_db"
DB_USER="postgres"
LOG_FILE="/var/log/postgresql/daily_vacuum_$(date +%Y%m%d).log"
echo "=== 开始每日 VACUUM 维护: $(date) ===" | tee -a "$LOG_FILE"
# 高频表优先 vacuum
TABLES=(
"supply_usage_records"
"supply_idempotency_records"
"supply_orders"
"supply_earnings"
)
for TABLE in "${TABLES[@]}"; do
echo "VACUUM $TABLE ..." | tee -a "$LOG_FILE"
vacuumdb -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \
--table "$TABLE" \
--analyze \
--verbose 2>&1 | tee -a "$LOG_FILE"
done
echo "=== VACUUM 维护完成: $(date) ===" | tee -a "$LOG_FILE"
4.2 每周维护脚本 (weekly_reindex.sh)
#!/bin/bash
# weekly_reindex.sh - 每周 REINDEX 维护
# 执行时间: 每周日 03:00
set -e
DB_HOST="localhost"
DB_PORT="5432"
DB_NAME="supply_db"
DB_USER="postgres"
LOG_FILE="/var/log/postgresql/weekly_reindex_$(date +%Y%m%d).log"
echo "=== 开始每周 REINDEX 维护: $(date) ===" | tee -a "$LOG_FILE"
# 检查并重建膨胀索引
膨胀索引=$(psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "
SELECT indexname FROM pg_stat_user_indexes
WHERE pg_relation_size(indexrelid) > 10 * 1024 * 1024
AND idx_scan = 0
AND schemaname = 'public';
")
for INDEX in $膨胀索引; do
echo "REINDEX INDEX $INDEX ..." | tee -a "$LOG_FILE"
reindexdb -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \
--index "$INDEX" \
--concurrently \
--verbose 2>&1 | tee -a "$LOG_FILE"
done
echo "=== REINDEX 维护完成: $(date) ===" | tee -a "$LOG_FILE"
4.3 Cron 任务配置
# /etc/cron.d/postgresql_maintenance
# 每日凌晨2点执行 vacuum
0 2 * * * postgres /home/postgres/scripts/daily_vacuum.sh
# 每周日凌晨3点执行 reindex
0 3 * * 0 postgres /home/postgres/scripts/weekly_reindex.sh
5. 监控指标
5.1 关键监控指标
| 指标 | 告警阈值 | 说明 |
|---|---|---|
| index膨胀率 | > 20% | 触发 REINDEX |
| dead_tuples | > 10000 | 触发 VACUUM |
| last_autovacuum | > 24h | 可能 autovacuum 异常 |
| idx_scan | = 0 | 索引未使用,考虑删除 |
5.2 监控查询
-- 检测需要维护的表
SELECT
schemaname,
relname AS table_name,
n_dead_tup,
n_live_tup,
last_autovacuum,
last_autoanalyze
FROM
pg_stat_user_tables
WHERE
n_dead_tup > 1000
ORDER BY
n_dead_tup DESC;
-- 检测未使用的索引
SELECT
schemaname,
tablename,
indexname,
idx_scan
FROM
pg_stat_user_indexes
WHERE
idx_scan = 0
AND NOT indexname LIKE '%_pkey'
ORDER BY
pg_relation_size(indexrelid) DESC;
6. 最佳实践
- 避免在高峰期维护: 维护操作安排在低峰期 (02:00-06:00)
- 优先自动 vacuum: 配置合理的 autovacuum 参数,减少手动干预
- 监控索引膨胀: 定期检测膨胀率,及时重建
- 使用 CONCURRENTLY: 关键表使用
REINDEX CONCURRENTLY避免锁表 - 保留维护日志: 记录每次维护执行情况,便于分析问题
7. 恢复时间预估
| 操作 | 表大小 | 预计耗时 | 锁类型 |
|---|---|---|---|
| VACUUM ANALYZE | 10GB | 5-10min | 轻量锁 |
| REINDEX | 1GB | 1-2min | 表锁* |
| REINDEX CONCURRENTLY | 1GB | 3-5min | 无锁 |
| VACUUM FULL | 10GB | 15-30min | 表锁 |
*使用 REINDEX CONCURRENTLY 可避免锁表
维护记录:
- v1.0 (2026-04-07): 初始版本