活动公告

系统通知
05-18 21:22
系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

PostgreSQL优化方案全面指南从基础配置调整到高级查询性能提升再到索引优化的完整数据库性能调优实战手册

SunJu_FaceMall

3万

主题

2860

科技点

3万

积分

白金月票

碾压王

积分
32872

塔罗立华奏

<font color=白金月票" /> 发表于 2025-9-11 09:10:00 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
引言

PostgreSQL是一个功能强大的开源关系型数据库管理系统,被广泛应用于各种规模的应用程序中。随着数据量的增长和用户访问的增加,数据库性能优化变得至关重要。本文旨在提供一个全面的PostgreSQL优化指南,从基础配置调整到高级查询性能提升,再到索引优化,帮助数据库管理员和开发人员掌握完整的数据库性能调优实战技能。

PostgreSQL性能评估基础

在开始优化之前,我们需要首先评估当前数据库的性能状况。这一步是为了确定性能瓶颈,为后续的优化工作提供方向。

性能指标监控

PostgreSQL提供了多种方式来监控性能指标:

• pg_stat_activity:查看当前活动的会话和查询
  1. SELECT * FROM pg_stat_activity;
复制代码

• pg_stat_database:数据库级别的统计信息
  1. SELECT * FROM pg_stat_database;
复制代码

• pg_stat_user_tables:用户表的统计信息
  1. SELECT * FROM pg_stat_user_tables;
复制代码

• pg_stat_user_indexes:用户索引的统计信息
  1. SELECT * FROM pg_stat_user_indexes;
复制代码

查询性能分析

使用EXPLAIN和EXPLAIN ANALYZE来分析查询执行计划:
  1. EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1;
复制代码

性能瓶颈识别

常见的性能瓶颈包括:

• CPU利用率高
• 内存不足
• 磁盘I/O瓶颈
• 网络延迟
• 锁等待
• 慢查询

基准测试

使用工具如pgbench进行基准测试:
  1. pgbench -i -s 50 mydb  # 初始化测试数据库
  2. pgbench -c 10 -j 4 -t 1000 mydb  # 运行基准测试
复制代码

基础配置调整

PostgreSQL的性能在很大程度上取决于配置参数的合理设置。这些参数通常在postgresql.conf文件中进行配置。

内存配置

shared_buffers参数设置PostgreSQL用于共享内存缓冲区的大小,这是最重要的内存参数之一。
  1. # 默认值通常较小,可以设置为系统内存的25%
  2. shared_buffers = 4GB
复制代码

work_mem指定用于排序和哈希操作的内存量。
  1. # 默认值通常为4MB,可以根据复杂查询需求增加
  2. work_mem = 16MB
复制代码

maintenance_work_mem用于维护操作(如VACUUM、CREATE INDEX等)的内存。
  1. # 可以设置为系统内存的5-10%
  2. maintenance_work_mem = 1GB
复制代码

effective_cache_size告诉PostgreSQL系统有多少内存可用于磁盘缓存。
  1. # 通常设置为系统内存的50-75%
  2. effective_cache_size = 12GB
复制代码

连接配置

max_connections设置最大并发连接数。
  1. # 根据应用需求设置,但不宜过高
  2. max_connections = 200
复制代码

使用连接池(如PgBouncer)来管理连接,减少连接开销。
  1. # PgBouncer配置示例
  2. [databases]
  3. mydb = host=localhost port=5432 dbname=mydb
  4. [pgbouncer]
  5. pool_mode = transaction
  6. max_client_conn = 1000
  7. default_pool_size = 20
复制代码

磁盘配置

wal_level设置WAL(Write-Ahead Logging)的级别。
  1. # 根据是否需要复制或归档设置
  2. wal_level = replica
复制代码

控制WAL文件的数量和大小。
  1. # PostgreSQL 10+
  2. max_wal_size = 4GB
  3. min_wal_size = 1GB
  4. # PostgreSQL 9.6及以下
  5. checkpoint_segments = 32
复制代码

random_page_cost估计非顺序获取磁盘页面的成本。
  1. # 对于SSD,可以设置较低的值
  2. random_page_cost = 1.1
复制代码

日志配置

记录执行时间超过指定值的SQL语句。
  1. # 记录执行时间超过1秒的查询
  2. log_min_duration_statement = 1000
复制代码

设置日志行的前缀格式。
  1. log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
复制代码

自动清理配置

自动清理死元数据。
  1. # 启用自动清理
  2. autovacuum = on
  3. # 自动清理阈值
  4. autovacuum_vacuum_threshold = 50
  5. autovacuum_analyze_threshold = 50
  6. # 自动清理比例
  7. autovacuum_vacuum_scale_factor = 0.2
  8. autovacuum_analyze_scale_factor = 0.1
复制代码

高级查询性能提升

在基础配置调整后,我们需要关注查询本身的性能优化。

查询计划分析

使用EXPLAIN分析查询计划:
  1. EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
  2. SELECT * FROM orders WHERE customer_id = 12345;
复制代码

关键指标包括:

• 执行时间
• 扫描类型(Seq Scan, Index Scan等)
• 行数估计与实际行数
• 缓冲区命中情况

• 全表扫描:对于大表,应避免全表扫描
• 嵌套循环:当连接表较大时,效率低下
• 排序操作:大表排序消耗大量资源
• 高估/低估行数:导致优化器选择不当的执行计划

SQL优化技巧
  1. -- INNER JOIN
  2. SELECT * FROM orders o INNER JOIN customers c ON o.customer_id = c.id;
  3. -- LEFT JOIN
  4. SELECT * FROM orders o LEFT JOIN customers c ON o.customer_id = c.id;
  5. -- LATERAL JOIN
  6. SELECT * FROM customers c, LATERAL (SELECT * FROM orders WHERE customer_id = c.id LIMIT 5) o;
复制代码
  1. WITH active_customers AS (
  2.     SELECT customer_id, COUNT(*) as order_count
  3.     FROM orders
  4.     WHERE order_date > CURRENT_DATE - INTERVAL '30 days'
  5.     GROUP BY customer_id
  6.     HAVING COUNT(*) > 5
  7. )
  8. SELECT * FROM customers c JOIN active_customers ac ON c.id = ac.customer_id;
复制代码
  1. -- 不推荐
  2. SELECT * FROM orders;
  3. -- 推荐
  4. SELECT id, customer_id, order_date, total_amount FROM orders;
复制代码
  1. -- 获取前10条记录
  2. SELECT * FROM orders ORDER BY order_date DESC LIMIT 10;
复制代码
  1. -- 不推荐:单行插入
  2. INSERT INTO orders (customer_id, order_date, total_amount) VALUES (1, NOW(), 100.00);
  3. INSERT INTO orders (customer_id, order_date, total_amount) VALUES (2, NOW(), 200.00);
  4. -- 推荐:批量插入
  5. INSERT INTO orders (customer_id, order_date, total_amount) VALUES
  6.     (1, NOW(), 100.00),
  7.     (2, NOW(), 200.00);
复制代码

并行查询

PostgreSQL支持并行查询,可以显著提高大型查询的性能。
  1. # 设置最大并行工作进程数
  2. max_parallel_workers = 8;
  3. # 设置每个查询的最大并行工作进程数
  4. max_parallel_workers_per_gather = 4;
  5. # 设置并行查询的最小表大小
  6. min_parallel_table_scan_size = 8MB;
  7. min_parallel_index_scan_size = 8MB;
复制代码
  1. -- 设置并行工作进程数
  2. SET max_parallel_workers_per_gather = 4;
  3. -- 执行并行查询
  4. EXPLAIN ANALYZE SELECT COUNT(*) FROM large_table;
复制代码

查询重写与优化
  1. -- 创建物化视图
  2. CREATE MATERIALIZED VIEW order_summary AS
  3. SELECT customer_id, COUNT(*) as order_count, SUM(total_amount) as total_spent
  4. FROM orders
  5. GROUP BY customer_id;
  6. -- 刷新物化视图
  7. REFRESH MATERIALIZED VIEW order_summary;
  8. -- 查询物化视图
  9. SELECT * FROM order_summary WHERE order_count > 10;
复制代码
  1. -- 为特定条件创建部分索引
  2. CREATE INDEX idx_active_users ON users (email) WHERE status = 'active';
复制代码
  1. -- 创建表达式索引
  2. CREATE INDEX idx_user_lower_email ON users (lower(email));
复制代码

查询优化工具

启用并使用pg_stat_statements扩展来跟踪SQL执行统计信息:
  1. -- 创建扩展
  2. CREATE EXTENSION pg_stat_statements;
  3. -- 查看最耗时的SQL
  4. SELECT query, calls, total_time, mean_time, rows
  5. FROM pg_stat_statements
  6. ORDER BY total_time DESC
  7. LIMIT 10;
复制代码

使用auto_explain扩展自动记录慢查询的执行计划:
  1. # postgresql.conf
  2. shared_preload_libraries = 'auto_explain'
  3. auto_explain.log_min_duration = '1s'
  4. auto_explain.log_analyze = true
  5. auto_explain.log_buffers = true
  6. auto_explain.log_format = 'json'
复制代码

索引优化

索引是提高查询性能的关键因素,但不当的索引使用可能导致性能下降。

索引类型

B-Tree是PostgreSQL中最常用的索引类型,适用于大多数情况。
  1. -- 创建B-Tree索引
  2. CREATE INDEX idx_users_email ON users (email);
  3. -- 复合B-Tree索引
  4. CREATE INDEX idx_orders_customer_date ON orders (customer_id, order_date);
复制代码

哈希索引适用于等值比较,但不支持范围查询。
  1. -- 创建哈希索引
  2. CREATE INDEX idx_users_username_hash ON users USING hash (username);
复制代码

GiST(Generalized Search Tree)索引适用于复杂数据类型和空间数据。
  1. -- 为几何数据创建GiST索引
  2. CREATE INDEX idx_locations_geom ON locations USING gist (geom);
复制代码

SP-GiST(Space-Partitioned GiST)适用于空间分区数据。
  1. -- 为电话号码创建SP-GiST索引
  2. CREATE INDEX idx_users_phone ON users USING spgist (phone_number);
复制代码

GIN(Generalized Inverted Index)索引适用于多值类型,如数组、jsonb和全文搜索。
  1. -- 为数组创建GIN索引
  2. CREATE INDEX idx_users_tags ON users USING gin (tags);
  3. -- 为jsonb创建GIN索引
  4. CREATE INDEX idx_products_attributes ON products USING gin (attributes);
复制代码

BRIN(Block Range Index)索引适用于线性排序的大表。
  1. -- 为按时间排序的大表创建BRIN索引
  2. CREATE INDEX idx_logs_created_at ON logs USING brin (created_at);
复制代码

索引策略

• 经常用于WHERE条件的列
• 经常用于JOIN条件的列
• 经常用于ORDER BY的列
• 经常用于GROUP BY的列

在复合索引中,列的顺序很重要:
  1. -- 好的复合索引顺序(高选择性列在前)
  2. CREATE INDEX idx_users_status_created ON users (status, created_at);
  3. -- 不好的复合索引顺序(低选择性列在前)
  4. CREATE INDEX idx_users_created_status ON users (created_at, status);
复制代码

只为表的一部分数据创建索引:
  1. -- 只为活跃用户创建索引
  2. CREATE INDEX idx_active_users_email ON users (email) WHERE status = 'active';
  3. -- 只为最近的数据创建索引
  4. CREATE INDEX idx_recent_orders ON orders (customer_id) WHERE order_date > CURRENT_DATE - INTERVAL '30 days';
复制代码

创建包含查询所需所有列的索引:
  1. -- 创建覆盖索引
  2. CREATE INDEX idx_orders_customer_total ON orders (customer_id) INCLUDE (total_amount);
  3. -- 查询可以使用覆盖索引,避免表访问
  4. SELECT customer_id, total_amount FROM orders WHERE customer_id = 12345;
复制代码

索引维护

定期重建索引以提高性能:
  1. -- 重建单个索引
  2. REINDEX INDEX idx_users_email;
  3. -- 重建表的所有索引
  4. REINDEX TABLE users;
  5. -- 并行重建索引
  6. REINDEX INDEX CONCURRENTLY idx_users_email;
复制代码

收集索引统计信息以帮助查询优化器:
  1. -- 分析表和索引
  2. ANALYZE users;
  3. -- 分析特定列
  4. ANALYZE users (email);
复制代码

监控索引使用情况:
  1. -- 查看索引使用统计
  2. SELECT * FROM pg_stat_user_indexes;
  3. -- 查找未使用的索引
  4. SELECT schemaname, tablename, indexname, idx_scan
  5. FROM pg_stat_user_indexes
  6. WHERE idx_scan = 0
  7. ORDER BY schemaname, tablename, indexname;
复制代码

索引优化技巧

过多的索引会降低写入性能并占用存储空间:
  1. -- 查找冗余索引
  2. SELECT pg_size_pretty(sum(pg_relation_size(idx))::bigint) as size,
  3.        (array_agg(idx))[1] as idx1, (array_agg(idx))[2] as idx2,
  4.        (array_agg(idx))[3] as idx3, (array_agg(idx))[4] as idx4
  5. FROM (
  6.     SELECT indexrelid::regclass as idx, indrelid::regclass as table,
  7.            array_to_string(indkey, ' ') as cols,
  8.            indpred is not null as partial
  9.     FROM pg_index
  10. ) sub
  11. GROUP BY table, cols, partial
  12. HAVING count(*) > 1;
复制代码
  1. -- 为不区分大小写的搜索创建表达式索引
  2. CREATE INDEX idx_users_lower_email ON users (lower(email));
  3. -- 使用表达式索引
  4. SELECT * FROM users WHERE lower(email) = 'test@example.com';
复制代码
  1. -- 为NULL值创建条件索引
  2. CREATE INDEX idx_users_optional_email ON users (email) WHERE email IS NOT NULL;
复制代码

表和分区优化

表设计和分区策略对数据库性能有重大影响。

表设计优化
  1. -- 使用适当的数据类型
  2. CREATE TABLE users (
  3.     id SERIAL PRIMARY KEY,
  4.     username VARCHAR(50) NOT NULL,
  5.     email VARCHAR(255) NOT NULL,
  6.     created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  7.     is_active BOOLEAN NOT NULL DEFAULT true,
  8.     login_count INTEGER NOT NULL DEFAULT 0
  9. );
复制代码
  1. -- 规范化设计(减少冗余)
  2. CREATE TABLE customers (
  3.     id SERIAL PRIMARY KEY,
  4.     name VARCHAR(100) NOT NULL,
  5.     email VARCHAR(255) NOT NULL UNIQUE
  6. );
  7. CREATE TABLE orders (
  8.     id SERIAL PRIMARY KEY,
  9.     customer_id INTEGER NOT NULL REFERENCES customers(id),
  10.     order_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  11.     total_amount NUMERIC(10,2) NOT NULL
  12. );
  13. -- 适度反规范化(提高查询性能)
  14. CREATE TABLE orders (
  15.     id SERIAL PRIMARY KEY,
  16.     customer_id INTEGER NOT NULL REFERENCES customers(id),
  17.     customer_name VARCHAR(100) NOT NULL, -- 冗余存储客户名称
  18.     order_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  19.     total_amount NUMERIC(10,2) NOT NULL
  20. );
复制代码
  1. -- 创建父表
  2. CREATE TABLE users (
  3.     id SERIAL PRIMARY KEY,
  4.     username VARCHAR(50) NOT NULL,
  5.     email VARCHAR(255) NOT NULL,
  6.     created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
  7. );
  8. -- 创建子表
  9. CREATE TABLE premium_users (
  10.     subscription_level VARCHAR(20) NOT NULL,
  11.     expiry_date DATE NOT NULL
  12. ) INHERITS (users);
  13. -- 查询所有用户(包括子表)
  14. SELECT * FROM users;
复制代码

表分区

表分区是将大表分割成更小、更易管理的部分的技术。
  1. -- 创建主表
  2. CREATE TABLE orders (
  3.     id SERIAL,
  4.     order_date TIMESTAMP WITH TIME ZONE NOT NULL,
  5.     customer_id INTEGER NOT NULL,
  6.     total_amount NUMERIC(10,2) NOT NULL
  7. ) PARTITION BY RANGE (order_date);
  8. -- 创建分区
  9. CREATE TABLE orders_2023_q1 PARTITION OF orders
  10.     FOR VALUES FROM ('2023-01-01') TO ('2023-04-01');
  11. CREATE TABLE orders_2023_q2 PARTITION OF orders
  12.     FOR VALUES FROM ('2023-04-01') TO ('2023-07-01');
  13. CREATE TABLE orders_2023_q3 PARTITION OF orders
  14.     FOR VALUES FROM ('2023-07-01') TO ('2023-10-01');
  15. CREATE TABLE orders_2023_q4 PARTITION OF orders
  16.     FOR VALUES FROM ('2023-10-01') TO ('2024-01-01');
复制代码
  1. -- 创建主表
  2. CREATE TABLE users (
  3.     id SERIAL,
  4.     username VARCHAR(50) NOT NULL,
  5.     email VARCHAR(255) NOT NULL,
  6.     country_code VARCHAR(2) NOT NULL
  7. ) PARTITION BY LIST (country_code);
  8. -- 创建分区
  9. CREATE TABLE users_us PARTITION OF users
  10.     FOR VALUES IN ('US');
  11. CREATE TABLE users_ca PARTITION OF users
  12.     FOR VALUES IN ('CA');
  13. CREATE TABLE users_uk PARTITION OF users
  14.     FOR VALUES IN ('UK');
  15. CREATE TABLE users_other PARTITION OF users
  16.     DEFAULT;
复制代码
  1. -- 创建主表
  2. CREATE TABLE logs (
  3.     id SERIAL,
  4.     log_time TIMESTAMP WITH TIME ZONE NOT NULL,
  5.     message TEXT NOT NULL
  6. ) PARTITION BY HASH (id);
  7. -- 创建分区
  8. CREATE TABLE logs_p0 PARTITION OF logs
  9.     FOR VALUES WITH (MODULUS 4, REMAINDER 0);
  10. CREATE TABLE logs_p1 PARTITION OF logs
  11.     FOR VALUES WITH (MODULUS 4, REMAINDER 1);
  12. CREATE TABLE logs_p2 PARTITION OF logs
  13.     FOR VALUES WITH (MODULUS 4, REMAINDER 2);
  14. CREATE TABLE logs_p3 PARTITION OF logs
  15.     FOR VALUES WITH (MODULUS 4, REMAINDER 3);
复制代码
  1. -- 创建新分区
  2. CREATE TABLE orders_2024_q1 PARTITION OF orders
  3.     FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
  4. -- 分离分区
  5. ALTER TABLE orders DETACH PARTITION orders_2023_q1;
  6. -- 附加分区
  7. ALTER TABLE orders ATTACH PARTITION orders_2023_q1
  8.     FOR VALUES FROM ('2023-01-01') TO ('2023-04-01');
复制代码

表维护

定期清理死元数据:
  1. -- 手动VACUUM
  2. VACUUM users;
  3. -- 分析表并更新统计信息
  4. VACUUM ANALYZE users;
  5. -- 并行VACUUM
  6. VACUUM (PARALLEL 4) users;
复制代码
  1. # 调整自动清理参数
  2. autovacuum_vacuum_threshold = 1000
  3. autovacuum_analyze_threshold = 500
  4. autovacuum_vacuum_scale_factor = 0.1
  5. autovacuum_analyze_scale_factor = 0.05
  6. autovacuum_vacuum_cost_limit = 2000
  7. autovacuum_vacuum_cost_delay = 10ms
复制代码
  1. -- 为特定表设置自动清理参数
  2. ALTER TABLE orders SET (
  3.     autovacuum_vacuum_threshold = 1000,
  4.     autovacuum_analyze_threshold = 500,
  5.     autovacuum_vacuum_scale_factor = 0.1,
  6.     autovacuum_analyze_scale_factor = 0.05
  7. );
复制代码

表空间管理

使用表空间将数据分布到不同的存储设备:
  1. -- 创建表空间
  2. CREATE TABLESPACE fast_space LOCATION '/mnt/fast_ssd/pgdata';
  3. CREATE TABLESPACE archive_space LOCATION '/mnt/slow_hdd/pgdata';
  4. -- 在特定表空间创建表
  5. CREATE TABLE active_data (
  6.     id SERIAL PRIMARY KEY,
  7.     data TEXT NOT NULL
  8. ) TABLESPACE fast_space;
  9. -- 将表移动到不同表空间
  10. ALTER TABLE archive_data SET TABLESPACE archive_space;
复制代码

高级特性优化

PostgreSQL提供了许多高级特性,可以用来提高性能。

复制优化
  1. # 主服务器配置
  2. wal_level = replica
  3. max_wal_senders = 5
  4. max_replication_slots = 5
  5. # 从服务器配置 (recovery.conf)
  6. standby_mode = on
  7. primary_conninfo = 'host=primary port=5432 user=replicator password=secret'
  8. recovery_target_timeline = 'latest'
复制代码
  1. -- 设置同步复制
  2. ALTER SYSTEM SET synchronous_commit = 'on';
  3. ALTER SYSTEM SET synchronous_standby_names = 'standby1,standby2';
  4. -- 重新加载配置
  5. SELECT pg_reload_conf();
复制代码
  1. -- 在发布服务器上创建发布
  2. CREATE PUBLICATION sales_publication FOR TABLE orders, customers;
  3. -- 在订阅服务器上创建订阅
  4. CREATE SUBSCRIPTION sales_subscription
  5. CONNECTION 'host=publisher dbname=sales user=replicator password=secret'
  6. PUBLICATION sales_publication;
复制代码

并行查询优化
  1. # 设置并行查询参数
  2. max_parallel_workers = 8
  3. max_parallel_workers_per_gather = 4
  4. max_parallel_maintenance_workers = 4
  5. parallel_tuple_cost = 0.1
  6. parallel_setup_cost = 1000.0
  7. min_parallel_table_scan_size = 8MB
  8. min_parallel_index_scan_size = 8MB
复制代码
  1. -- 使用并行查询
  2. SET max_parallel_workers_per_gather = 4;
  3. -- 创建适合并行查询的表
  4. CREATE TABLE large_table (
  5.     id SERIAL PRIMARY KEY,
  6.     data TEXT NOT NULL
  7. ) WITH (parallel_workers = 4);
  8. -- 并行创建索引
  9. CREATE INDEX CONCURRENTLY idx_large_table_data ON large_table (data) WITH (parallel_workers = 4);
复制代码

连接池优化
  1. # PgBouncer配置
  2. [databases]
  3. mydb = host=localhost port=5432 dbname=mydb
  4. [pgbouncer]
  5. pool_mode = transaction
  6. max_client_conn = 1000
  7. default_pool_size = 20
  8. reserve_pool = 5
  9. reserve_pool_timeout = 3
  10. server_idle_timeout = 30
复制代码

• 使用事务池模式处理短事务
• 使用会话池模式处理长事务
• 根据应用负载调整池大小
• 监控连接池使用情况

缓存优化
  1. # 配置PostgreSQL缓存
  2. shared_buffers = 4GB
  3. effective_cache_size = 12GB
复制代码
  1. # Python示例:使用Redis缓存查询结果
  2. import redis
  3. import psycopg2
  4. import json
  5. # 连接Redis
  6. redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
  7. # 连接PostgreSQL
  8. pg_conn = psycopg2.connect("dbname=mydb user=myuser password=mypassword")
  9. def get_user_with_cache(user_id):
  10.     # 尝试从Redis缓存获取
  11.     cached_user = redis_client.get(f"user:{user_id}")
  12.     if cached_user:
  13.         return json.loads(cached_user)
  14.    
  15.     # 从数据库获取
  16.     cursor = pg_conn.cursor()
  17.     cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
  18.     user = cursor.fetchone()
  19.    
  20.     # 存入Redis缓存,过期时间1小时
  21.     if user:
  22.         redis_client.setex(f"user:{user_id}", 3600, json.dumps(user))
  23.    
  24.     return user
复制代码

查询优化扩展
  1. -- 安装pg_hint_plan扩展
  2. CREATE EXTENSION pg_hint_plan;
  3. -- 使用提示优化查询
  4. /*+ IndexScan(users idx_users_email) */
  5. SELECT * FROM users WHERE email = 'test@example.com';
复制代码
  1. -- 安装pg_prewarm扩展
  2. CREATE EXTENSION pg_prewarm;
  3. -- 预热表到缓存
  4. SELECT pg_prewarm('users');
  5. -- 预热索引到缓存
  6. SELECT pg_prewarm('idx_users_email');
复制代码

监控与维护

持续监控和定期维护是保持PostgreSQL高性能的关键。

性能监控
  1. -- 创建扩展
  2. CREATE EXTENSION pg_stat_statements;
  3. -- 查看最耗时的查询
  4. SELECT query, calls, total_time, mean_time, rows
  5. FROM pg_stat_statements
  6. ORDER BY total_time DESC
  7. LIMIT 10;
  8. -- 重置统计信息
  9. SELECT pg_stat_statements_reset();
复制代码
  1. -- 查看当前活动会话
  2. SELECT pid, datname, usename, application_name, client_addr, state, query
  3. FROM pg_stat_activity
  4. WHERE state != 'idle';
  5. -- 查看长时间运行的查询
  6. SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state
  7. FROM pg_stat_activity
  8. WHERE (now() - pg_stat_activity.query_start) > interval '5 minutes';
复制代码
  1. -- 安装扩展
  2. CREATE EXTENSION pg_stat_kcache;
  3. -- 查看查询资源使用情况
  4. SELECT query, calls, total_time, mean_time,
  5.        shared_blks_hit, shared_blks_read,
  6.        local_blks_hit, local_blks_read,
  7.        temp_blks_read, temp_blks_written
  8. FROM pg_stat_statements
  9. ORDER BY total_time DESC
  10. LIMIT 10;
复制代码

系统监控
  1. # 安装pg_top
  2. sudo apt-get install ptop
  3. # 运行pg_top
  4. pg_top -d mydb -U myuser
复制代码
  1. # 安装pgBadger
  2. sudo apt-get install pgbadger
  3. # 生成报告
  4. pgbadger /var/log/postgresql/postgresql-*.log -o report.html
复制代码
  1. # prometheus.yml配置示例
  2. scrape_configs:
  3.   - job_name: 'postgres'
  4.     static_configs:
  5.       - targets: ['localhost:9187']
复制代码

定期维护任务
  1. -- 配置自动VACUUM
  2. ALTER SYSTEM SET autovacuum = on;
  3. ALTER SYSTEM SET autovacuum_vacuum_threshold = 1000;
  4. ALTER SYSTEM SET autovacuum_analyze_threshold = 500;
  5. -- 重新加载配置
  6. SELECT pg_reload_conf();
复制代码
  1. -- 重建表的所有索引
  2. REINDEX TABLE users;
  3. -- 并行重建索引
  4. REINDEX INDEX CONCURRENTLY idx_users_email;
复制代码
  1. -- 更新表统计信息
  2. ANALYZE users;
  3. -- 更新特定列统计信息
  4. ANALYZE users (email);
复制代码

数据库健康检查
  1. -- 安装pgstattuple扩展
  2. CREATE EXTENSION pgstattuple;
  3. -- 检查表膨胀
  4. SELECT * FROM pgstattuple('users');
复制代码
  1. -- 查找未使用的索引
  2. SELECT schemaname, tablename, indexname, idx_scan
  3. FROM pg_stat_user_indexes
  4. WHERE idx_scan = 0
  5. ORDER BY schemaname, tablename, indexname;
复制代码
  1. -- 查找长事务
  2. SELECT pid, now() - xact_start AS duration, state, query
  3. FROM pg_stat_activity
  4. WHERE (now() - xact_start) > interval '1 hour'
  5. AND state != 'idle';
复制代码

实战案例

通过实际案例展示PostgreSQL优化过程。

案例一:电商网站订单查询优化

某电商网站订单查询缓慢,特别是在高峰期。
  1. -- 慢查询示例
  2. SELECT o.id, o.order_date, c.name, o.total_amount
  3. FROM orders o
  4. JOIN customers c ON o.customer_id = c.id
  5. WHERE o.order_date BETWEEN '2023-01-01' AND '2023-01-31'
  6. ORDER BY o.order_date DESC
  7. LIMIT 100;
复制代码
  1. EXPLAIN (ANALYZE, BUFFERS)
  2. SELECT o.id, o.order_date, c.name, o.total_amount
  3. FROM orders o
  4. JOIN customers c ON o.customer_id = c.id
  5. WHERE o.order_date BETWEEN '2023-01-01' AND '2023-01-31'
  6. ORDER BY o.order_date DESC
  7. LIMIT 100;
复制代码

发现执行计划中使用了全表扫描,且排序操作消耗大量资源。

1. 创建适当的索引:
  1. -- 为订单日期创建索引
  2. CREATE INDEX idx_orders_order_date ON orders (order_date DESC);
  3. -- 为客户ID创建索引
  4. CREATE INDEX idx_orders_customer_id ON orders (customer_id);
  5. -- 创建复合索引
  6. CREATE INDEX idx_orders_date_customer ON orders (order_date DESC, customer_id);
复制代码

1. 优化查询:
  1. -- 使用覆盖索引减少表访问
  2. CREATE INDEX idx_orders_date_customer_amount ON orders (order_date DESC, customer_id) INCLUDE (total_amount);
  3. -- 优化后的查询
  4. SELECT o.id, o.order_date, c.name, o.total_amount
  5. FROM orders o
  6. JOIN customers c ON o.customer_id = c.id
  7. WHERE o.order_date BETWEEN '2023-01-01' AND '2023-01-31'
  8. ORDER BY o.order_date DESC
  9. LIMIT 100;
复制代码

1. 配置优化:
  1. # 调整work_mem以优化排序操作
  2. work_mem = 32MB
  3. # 增加共享缓冲区
  4. shared_buffers = 4GB
复制代码
  1. EXPLAIN (ANALYZE, BUFFERS)
  2. SELECT o.id, o.order_date, c.name, o.total_amount
  3. FROM orders o
  4. JOIN customers c ON o.customer_id = c.id
  5. WHERE o.order_date BETWEEN '2023-01-01' AND '2023-01-31'
  6. ORDER BY o.order_date DESC
  7. LIMIT 100;
复制代码

查询时间从原来的5秒降低到100毫秒,性能提升显著。

案例二:日志分析系统优化

某日志分析系统在处理大量日志数据时性能低下,查询响应时间长。
  1. -- 查看表大小
  2. SELECT pg_size_pretty(pg_total_relation_size('logs'));
  3. -- 查看表行数
  4. SELECT COUNT(*) FROM logs;
  5. -- 检查索引使用情况
  6. SELECT * FROM pg_stat_user_indexes WHERE relname = 'logs';
复制代码

发现日志表非常大,且现有索引不足以支持高效查询。

1. 实施表分区:
  1. -- 创建分区表
  2. CREATE TABLE logs (
  3.     id SERIAL,
  4.     log_time TIMESTAMP WITH TIME ZONE NOT NULL,
  5.     level VARCHAR(10) NOT NULL,
  6.     message TEXT NOT NULL,
  7.     source VARCHAR(100) NOT NULL
  8. ) PARTITION BY RANGE (log_time);
  9. -- 创建按月分区
  10. CREATE TABLE logs_2023_01 PARTITION OF logs
  11.     FOR VALUES FROM ('2023-01-01') TO ('2023-02-01');
  12. CREATE TABLE logs_2023_02 PARTITION OF logs
  13.     FOR VALUES FROM ('2023-02-01') TO ('2023-03-01');
复制代码

1. 创建适当索引:
  1. -- 为分区表创建索引
  2. CREATE INDEX idx_logs_2023_01_log_time ON logs_2023_01 (log_time);
  3. CREATE INDEX idx_logs_2023_01_level ON logs_2023_01 (level);
  4. CREATE INDEX idx_logs_2023_01_source ON logs_2023_01 (source);
  5. CREATE INDEX idx_logs_2023_02_log_time ON logs_2023_02 (log_time);
  6. CREATE INDEX idx_logs_2023_02_level ON logs_2023_02 (level);
  7. CREATE INDEX idx_logs_2023_02_source ON logs_2023_02 (source);
复制代码

1. 使用BRIN索引:
  1. -- 为时间列创建BRIN索引
  2. CREATE INDEX idx_logs_log_time_brin ON logs USING brin (log_time);
复制代码

1. 配置并行查询:
  1. # 启用并行查询
  2. max_parallel_workers_per_gather = 4
  3. min_parallel_table_scan_size = 8MB
复制代码
  1. -- 测试查询性能
  2. EXPLAIN (ANALYZE, BUFFERS)
  3. SELECT COUNT(*) FROM logs
  4. WHERE log_time BETWEEN '2023-01-01' AND '2023-01-31'
  5. AND level = 'ERROR';
  6. -- 测试聚合查询
  7. EXPLAIN (ANALYZE, BUFFERS)
  8. SELECT level, COUNT(*)
  9. FROM logs
  10. WHERE log_time BETWEEN '2023-01-01' AND '2023-01-31'
  11. GROUP BY level;
复制代码

查询性能提升明显,聚合查询从原来的30秒降低到2秒。

案例三:社交媒体平台用户活动分析优化

某社交媒体平台需要分析用户活动,但相关查询非常缓慢。
  1. -- 慢查询示例
  2. SELECT u.id, u.username, COUNT(a.id) as activity_count
  3. FROM users u
  4. LEFT JOIN activities a ON u.id = a.user_id
  5. WHERE u.created_at BETWEEN '2023-01-01' AND '2023-01-31'
  6. GROUP BY u.id, u.username
  7. ORDER BY activity_count DESC
  8. LIMIT 100;
复制代码
  1. -- 检查表大小
  2. SELECT pg_size_pretty(pg_total_relation_size('users'));
  3. SELECT pg_size_pretty(pg_total_relation_size('activities'));
  4. -- 分析执行计划
  5. EXPLAIN (ANALYZE, BUFFERS)
  6. SELECT u.id, u.username, COUNT(a.id) as activity_count
  7. FROM users u
  8. LEFT JOIN activities a ON u.id = a.user_id
  9. WHERE u.created_at BETWEEN '2023-01-01' AND '2023-01-31'
  10. GROUP BY u.id, u.username
  11. ORDER BY activity_count DESC
  12. LIMIT 100;
复制代码

发现activities表非常大,且JOIN操作和排序消耗大量资源。

1. 创建物化视图:
  1. -- 创建物化视图存储用户活动统计
  2. CREATE MATERIALIZED VIEW user_activity_stats AS
  3. SELECT u.id, u.username, COUNT(a.id) as activity_count
  4. FROM users u
  5. LEFT JOIN activities a ON u.id = a.user_id
  6. GROUP BY u.id, u.username;
  7. -- 创建索引
  8. CREATE INDEX idx_user_activity_stats_count ON user_activity_stats (activity_count DESC);
  9. CREATE INDEX idx_user_activity_stats_id ON user_activity_stats (id);
复制代码

1. 定期刷新物化视图:
  1. -- 刷新物化视图
  2. REFRESH MATERIALIZED VIEW CONCURRENTLY user_activity_stats;
复制代码

1. 使用查询优化:
  1. -- 优化后的查询
  2. SELECT * FROM user_activity_stats
  3. ORDER BY activity_count DESC
  4. LIMIT 100;
复制代码

1. 使用并行查询:
  1. -- 启用并行查询
  2. SET max_parallel_workers_per_gather = 4;
  3. -- 使用并行聚合
  4. EXPLAIN (ANALYZE, BUFFERS)
  5. SELECT u.id, u.username, COUNT(a.id) as activity_count
  6. FROM users u
  7. LEFT JOIN activities a ON u.id = a.user_id
  8. WHERE u.created_at BETWEEN '2023-01-01' AND '2023-01-31'
  9. GROUP BY u.id, u.username
  10. ORDER BY activity_count DESC
  11. LIMIT 100;
复制代码
  1. -- 测试优化后的查询
  2. EXPLAIN (ANALYZE, BUFFERS)
  3. SELECT * FROM user_activity_stats
  4. ORDER BY activity_count DESC
  5. LIMIT 100;
复制代码

查询时间从原来的45秒降低到50毫秒,性能提升显著。

总结与最佳实践

PostgreSQL优化总结

PostgreSQL优化是一个系统性的过程,涉及多个层面:

1. 基础配置调整:合理设置内存、连接、磁盘等参数
2. 查询性能优化:分析执行计划,优化SQL语句
3. 索引优化:选择合适的索引类型和策略
4. 表和分区优化:合理设计表结构,实施分区策略
5. 高级特性优化:利用复制、并行查询等高级特性
6. 监控与维护:持续监控性能,定期维护数据库

最佳实践

• 根据系统资源合理设置shared_buffers(通常为系统内存的25%)
• 适当增加work_mem以优化排序和哈希操作
• 根据磁盘类型调整random_page_cost(SSD可设为1.1)
• 启用并配置自动清理(autovacuum)

• 使用EXPLAIN ANALYZE分析查询执行计划
• 避免全表扫描,确保使用适当的索引
• 使用LIMIT限制结果集大小
• 批量操作代替单行操作
• 使用CTE(Common Table Expressions)提高复杂查询可读性和性能

• 为经常用于WHERE、JOIN、ORDER BY和GROUP BY的列创建索引
• 使用复合索引优化多列查询
• 考虑使用部分索引减少索引大小
• 定期维护索引,重建膨胀的索引
• 监控索引使用情况,删除未使用的索引

• 选择合适的数据类型
• 适度规范化与反规范化
• 对大表实施分区策略
• 使用继承表处理特殊类型的数据
• 定期VACUUM和ANALYZE表

• 使用pg_stat_statements监控查询性能
• 定期检查表膨胀和索引使用情况
• 设置适当的日志级别,记录慢查询
• 使用外部工具(如pgBadger、Prometheus)进行监控
• 制定定期维护计划,包括VACUUM、ANALYZE和索引重建

持续优化

PostgreSQL优化不是一次性的任务,而是一个持续的过程:

1. 定期评估数据库性能
2. 监控系统资源使用情况
3. 分析慢查询并优化
4. 根据数据增长调整配置
5. 跟踪PostgreSQL版本更新,利用新特性

通过遵循本文提供的指南和最佳实践,您可以显著提高PostgreSQL数据库的性能,确保应用程序能够高效、稳定地运行。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

0

主题

1304

科技点

654

积分

候风辨气

积分
654
候风辨气 发表于 2025-9-11 09:47:30 | 显示全部楼层
感謝分享
温馨提示:看帖回帖是一种美德,您的每一次发帖、回帖都是对论坛最大的支持,谢谢! [这是默认签名,点我更换签名]
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则