性能优化
PostgreSQL 生产数据库性能优化与高可用运维
当需要设计、优化、维护或排障 PostgreSQL 数据库(索引、查询、分区、复制、备份、监控)时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
PostgreSQL 生产数据库性能优化通用
解决思路
- 理解 PostgreSQL 进程架构:postmaster 管理连接,每个客户端连接对应一个 backend 进程,共享内存包含 shared buffers、WAL buffers 和锁表,后台 worker 负责 autovacuum、checkpointer、WAL writer 和统计收集,WAL 用于持久化和复制,存储层用 TOAST 存大值、FSM 管理空闲空间、VM 管理可见性。
- 掌握 MVCC 机制:每个事务看到一致快照,更新产生新行版本,用 xmin/xmax 标记事务,读不阻塞写、写不阻塞读,必须定期 VACUUM 回收死元组并防止事务 ID 回卷,FREEZE 将老行标记为对所有事务可见。
- 按需选择事务隔离级别:Read Committed 为默认且性能最好,适合多数应用;Repeatable Read 适合需要一致性的报表和分析;Serializable 使用 SSI 实现真正串行化,适合金融等关键一致性场景。
- 按查询模式选择索引类型:B-Tree 用于等值、范围、排序;Hash 仅用于等值;GiST 用于几何、全文、范围;GIN 用于数组、JSONB、全文;BRIN 用于自然有序的超大表;SP-GiST 用于非平衡结构如四叉树、k-d 树、IP 地址。
- 理解查询规划器:依赖统计信息(n_distinct、correlation、most_common_vals、histogram_bounds)估算基数,用成本模型选择顺序扫描、索引扫描、位图扫描及嵌套循环、哈希、归并连接,并通过查询重写、谓词下推、连接重排序优化。
- 阅读 EXPLAIN 输出:关注 cost(启动成本..总成本)、rows(估算行数)、width(平均行宽)、actual time(实际时间)、loops(节点执行次数),用 EXPLAIN ANALYZE 获取真实执行数据。
- 按场景选择分区策略:Range 用于时间序列和顺序值,List 用于离散分类值,Hash 用于均匀分布;利用分区裁剪消除无关分区,使用分区级连接、分区级聚合和并行分区处理。
- 配置复制与高可用:流复制(物理 WAL 流)用于高可用和读扩展,可选异步、同步或法定人数模式;逻辑复制(行级变更流)用于选择性复制、升级和多主;级联复制让备库从备库复制以减轻主库负载。
- 使用连接池:pgBouncer 轻量,支持 session、transaction、statement 三种池化模式;PgPool-II 功能丰富,支持连接池、负载均衡、查询缓存和读写分离。
- 执行关键维护操作:VACUUM 回收死元组空间并更新统计(常规 VACUUM 非阻塞),ANALYZE 更新规划器统计,REINDEX 重建索引修复膨胀(PG 12+ 用 REINDEX CONCURRENTLY),autovacuum 基于阈值自动触发并需调优。
- 调优关键配置参数:shared_buffers 设为 RAM 的 25% 起步,effective_cache_size 为 RAM 的 50-75%,work_mem 控制排序/哈希每操作内存,maintenance_work_mem 控制 VACUUM 和 CREATE INDEX 内存;调整 checkpoint_timeout、max_wal_size、checkpoint_completion_target、wal_buffers;调整 random_page_cost、effective_io_concurrency、default_statistics_target;设置 max_connections 和连接限制。
- 设计复合索引:等值列在前、排序/范围列在后、高选择性列在前,并严格匹配查询模式,例如 WHERE status='active' AND created_at>'2024-01-01' ORDER BY created_at 应建 (status, created_at)。
- 使用部分索引只索引子集行以减小索引体积、加快非索引行更新,例如 WHERE deleted_at IS NULL、WHERE created_at > NOW() - INTERVAL '90 days'、WHERE status IN ('pending','processing')。
- 使用表达式索引索引计算值,例如 LOWER(email)、DATE(created_at)、(data->>'status')。
- 使用覆盖索引 INCLUDE 非键列实现仅索引扫描,例如 CREATE INDEX ... ON users(email) INCLUDE (first_name, last_name, created_at)。
- 监控索引使用与膨胀:查询 pg_stat_user_indexes 找出 idx_scan=0 的未使用索引,按 pg_relation_size 排序评估索引大小、idx_scan、idx_tup_read、idx_tup_fetch 检测膨胀。
- 用 EXPLAIN ANALYZE 分析查询:关注 Planning Time、Execution Time、Shared Hit vs Read、估算与实际行数差异、Filter vs Index Cond,使用 (ANALYZE, BUFFERS, VERBOSE) 获取详细输出。
- 避免常见查询反模式:消除 N+1 查询(用 JOIN 或批量)、避免 SELECT *、避免隐式类型转换导致索引失效、避免在索引列上使用函数、用 IN 替代多个 OR 条件。
- 优化连接:嵌套循环适合小外表加有索引的内表,哈希连接适合大表无好索引的等值连接,归并连接适合预排序数据的等值连接;理解规划器会重排连接顺序,可用 join_collapse_limit 控制。
- 优化聚合:使用分区级聚合、哈希聚合、排序聚合和并行聚合;对昂贵聚合使用物化视图并按计划或触发器刷新,以新鲜度换查询速度。
- 利用多级查询缓存:shared buffers、OS 页缓存、应用层缓存(Redis/Memcached)、预编译语句复用查询计划。
- 实现范围分区:用 PARTITION BY RANGE (created_at) 建分区表,按月创建分区,建默认分区兜底,并在各分区上建索引。
- 自动化分区管理:编写 plpgsql 函数用 format 动态创建月度分区和索引,用 IF NOT EXISTS 保证幂等。
- 维护分区:用 ALTER TABLE ... DETACH PARTITION 快速非阻塞分离旧分区,再 DROP 或先归档到 archive schema 再删除。
- 搭建流复制:主库设置 wal_level=replica、max_wal_senders、max_replication_slots、hot_standby=on、synchronous_commit 和 synchronous_standby_names;创建 REPLICATION 用户;在 pg_hba.conf 允许复制连接;备库用 pg_basebackup -R -X stream -C -S 做基础备份并自动生成 primary_conninfo 和 primary_slot_name。
- 监控复制:主库查询 pg_stat_replication 看 client_addr、state、sync_state、replay_lag,查询 pg_replication_slots 看 slot 状态;备库用 now() - pg_last_xact_replay_timestamp() 计算延迟,用 pg_is_in_recovery() 判断恢复状态。
- 执行故障切换与计划切换:用 pg_ctl promote 或 SELECT pg_promote() 提升备库;计划切换时先停止主库写入、等备库追平、提升备库、再将旧主库重配为新备库。
- 搭建逻辑复制:发布端用 CREATE PUBLICATION 指定表或 FOR ALL TABLES;订阅端用 CREATE SUBSCRIPTION 指定连接串和发布名,用 pg_stat_subscription 监控。
- 执行物理备份:用 pg_basebackup 做全量备份(-F tar -z -P -v),用 -X stream 同时备份 WAL 以支持 PITR;配置 archive_mode=on 和 archive_command 实现 WAL 连续归档。
- 执行逻辑备份:用 pg_dump -F c 备份单库、pg_dumpall 备份所有库、-t 指定表、--schema-only 仅备份结构;用 pg_restore -j 4 并行恢复、-t 恢复指定表。
- 实施 PITR:先做基础备份并配置 WAL 归档,恢复时解压基础备份、创建 recovery.signal、配置 restore_command 和 recovery_target_time/name/lsn,再启动 PostgreSQL。
- 遵循备份策略:3-2-1 规则(3 份副本、2 种介质、1 份异地),每日增量 WAL 归档、每周全量 pg_basebackup、每月长期保留,并定期恢复到测试环境验证完整性和恢复时间。
- 监控关键指标:数据库健康(活跃连接、事务率、缓存命中率、死锁、检查点频率、autovacuum 运行)、查询性能(慢查询日志、执行时间、锁等待、顺序扫描)、系统资源(CPU、内存、磁盘 I/O、网络)。
- 使用核心监控查询:用 pg_stat_activity 统计总连接/活跃/空闲/事务中空闲;用 pg_statio_user_tables 计算缓存命中率;用 pg_stat_user_tables 检测表膨胀(n_dead_tup、n_live_tup、dead_ratio);用 pg_stat_activity 找长运行查询;用 pg_locks 和 pg_stat_activity 关联找出阻塞与被阻塞查询。
- 安装并使用 pg_stat_statements:CREATE EXTENSION,配置 shared_preload_libraries、track=all、max=10000,按 total_exec_time 和 mean_exec_time 排序找出最耗时的查询。
- 遵循 schema 设计最佳实践:事务系统规范化到 3NF,读密集场景选择性反规范化,用外键保证完整性,超大表考虑分区;选择最小合适数据类型(BIGINT/INTEGER、NUMERIC、TIMESTAMPTZ、TEXT 优先于 VARCHAR、UUID、JSONB);为所有表建主键、外键、CHECK、NOT NULL、UNIQUE 约束并命名。
- 实施零停机迁移:先 ADD COLUMN,再分批回填数据(如 LIMIT 10000),最后加 NOT NULL 约束;生产环境用 CREATE INDEX CONCURRENTLY 避免锁表并用 pg_stat_progress_create_index 监控;大表修改用 pg_repack,修改前先分区,安排在维护窗口并先在类生产数据上测试。
- 遵循安全最佳实践:用强密码或证书认证、SCRAM-SHA-256 加密、为不同应用分配独立用户、应用连接避免超级用户;最小权限授权、基于角色访问控制、撤销 PUBLIC 权限、多租户用行级安全;严格配置 pg_hba.conf、使用 SSL/TLS、防火墙封锁端口、复制走 VPN 或私有网络;启用连接日志、记录 DDL、用 pgAudit 详细审计并监控可疑活动。
- 执行维护计划:每日监控慢查询、复制延迟、autovacuum 活动和磁盘空间;每周分析 top 查询、审查索引使用、检查膨胀、验证备份;每月对关键表全量 VACUUM、REINDEX 膨胀索引、审查配置、容量规划;每季度审查优化索引、schema 优化、升级规划、更新性能基线。
- 启用并行查询:配置 max_parallel_workers_per_gather、max_parallel_workers、parallel_setup_cost、parallel_tuple_cost、min_parallel_table_scan_size,对大型顺序扫描、大聚合、大表哈希连接、位图堆扫描启用并行。
- 编写存储过程和函数:用 plpgsql 创建 PROCEDURE 执行批量更新并 COMMIT;用 FUNCTION 配合 RETURNING INTO 返回 ID,并用 EXCEPTION 捕获 unique_violation 等错误并 RAISE EXCEPTION 输出 SQLERRM。
- 使用外部数据包装器:CREATE EXTENSION postgres_fdw,创建 SERVER 指定远端主机/库/端口,创建 USER MAPPING,用 IMPORT FOREIGN SCHEMA 导入外部表并直接查询。
- 优化 JSONB:用 GIN 索引支持包含查询,用 jsonb_path_ops 使索引更小更快,用表达式索引索引特定字段;用 @> 包含查询、? 存在查询、-> 和 ->> 路径查询、@> 数组查询以命中索引。
- 实现全文搜索:添加 tsvector 列,用 to_tsvector 生成向量,建 GIN 索引,用 tsvector_update_trigger 触发器自动更新;用 to_tsquery 查询并按 ts_rank 排序,用 phraseto_tsquery 做短语搜索,用 ts_headline 生成高亮摘要。
- 排障常见问题:慢查询用 EXPLAIN ANALYZE、验证索引、ANALYZE 更新统计、检查外键缺失索引和索引列上的函数;高 CPU 用 pg_stat_statements 找昂贵查询、检查缺失索引、审查并行设置;连接耗尽则增大 max_connections、引入 pgBouncer、排查连接泄漏;autovacuum 跟不上则增加 worker、调整阈值、减小 naptime、增大 work_mem、排查长事务;复制延迟则检查网络带宽、备库资源、备库长查询、WAL 生成速率、wal_sender_timeout;事务 ID 回卷则监控最老事务年龄、对老表 VACUUM FREEZE、检查 autovacuum_freeze_max_age、提高 autovacuum 激进度。
- 使用诊断查询:用 pg_constraint 和 pg_index 关联找出外键上缺失的索引;用 pg_blocking_pids 关联 pg_stat_activity 找出阻塞查询。
适用边界 · 注意事项
- 需要非 PostgreSQL 数据库(如 MySQL、MongoDB)的场景
- 仅做简单 CRUD 且无性能、高可用或规模需求的小型应用
- 不具备 Linux 命令行和 SQL 基础的使用者