2026-02-24 20:49:25
PostgreSQL随机慢查询分析过程记录如下:
问题描述Web功能偶然长时间无响应,后端日志显示查询未返回结果。该查询为多表关联查询,涉及五张表关联及两个子查询构造的临时表,开启慢查询日志后记录到完整SQL语句及参数。测试发现选中特定城市时出现慢查询概率较高。
排查过程排除GIN索引问题
检查五张表索引类型,确认均为B-tree索引,未使用GIN索引。
执行以下SQL验证:
select * from pg_indexes where indexdef like '%gin%';EXPLAIN分析
替换SQL中的参数(如date_part函数)后执行EXPLAIN (ANALYZE, VERBOSE)。
观察输出中的cost和actual time,未发现明显优化点。
检查表状态
查询表元组状态,确认无异常:
SELECT n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname = 'table_name';查看PostgreSQL日志
日志路径为/var/log/postgresql,仅发现一条客户端连接重置日志,无其他线索:
2021-05-19 15:10:53.758 CST [] @ LOG: could not receive data from client: Connection reset by peer锁等待排查
配置log_lock_waits和deadlock_timeout记录锁等待情况。
重新加载配置:
SELECT pg_reload_conf();触发慢查询后日志未显示锁等待,排除锁问题。
重建索引
对两张小表执行重建索引命令:
REINDEX TABLE my_table;观察后无效,排除索引碎片问题。
监控pg_stat_activity
定时查询活动会话,发现慢查询时存在以下现象:
三条相同SQL语句,其中两条为background worker,一条为client backend。
background worker的wait_event_type为IO,wait_event为DataFileRead,表明在等待数据读取到内部缓存。
后续client backend变为等待BgWorkerShutdown。
措施一:应用层分页
数据库返回全部结果(最多几千条),在Python程序中处理分页:
total = len(records)page_result = [records[i:i + size] for i in range(0, len(records), size)]if page <= len(page_result): records = page_result[page - 1]else: if total > 0: records = page_result[0]措施二:使用row_number()
在SQLAlchemy中通过func.row_number()实现分页,需确保排序一致:
row_number_column = func.row_number().over( partition_by=Foo.foo_field, order_by=desc(Foo.foo_date_time)).label('row_number')query = session.query(Foo).filter(Foo.time_key <= time_key).add_column(row_number_column)query = query.from_self().filter(row_number_column == 1)替代方案:避免LIMIT + OFFSET
改用sort_column > 'last_seen_value' LIMIT 100,通过记录上次查询的最后值实现分页,提升性能。