← 中文资源

简体中文版本

为数据库查询设置强制超时

按角色和事务设置 statement_timeout、lock_timeout 与空闲事务超时,防止单条慢查询占满连接池。

阅读英文原文

概述

连接池槽位固定,如果单条查询可以无限执行,几条慢语句就能占满全部连接,把局部变慢放大为整体不可用。PostgreSQL 提供三层互补限制:statement_timeout 限制语句执行时间,lock_timeout 限制等待锁的时间,idle_in_transaction_session_timeout 关闭持有快照却空闲的事务。

步骤

  1. 在角色级别设置默认值,新会话自动继承,无需改应用代码:
ALTER ROLE app_rw SET statement_timeout = '5s';
ALTER ROLE app_rw SET lock_timeout = '2s';
ALTER ROLE app_rw SET idle_in_transaction_session_timeout = '30s';
  1. 报表等确实需要更长时间的场景,只在事务内用 SET LOCAL 放宽,提交或回滚后自动恢复,不会污染复用的连接:
BEGIN;
SET LOCAL statement_timeout = '120s';
SELECT region, sum(total) FROM orders GROUP BY region;
COMMIT;
  1. 在应用层区分 SQLSTATE:57014 表示语句被取消,55P03 表示等锁超时。前者返回可重试的 503,后者返回 409,避免笼统的 500。
  2. 用 psql -U app_rw -d appdb -c 'SELECT pg_sleep(10);' 验证限制生效。

验证

预期上述语句在约 5 秒后报 ERROR: canceling statement due to statement timeout。随后确认正常接口 p99 未受影响,并检查超时计数是否按查询名导出。若超时率突然上升,通常意味着缺少索引、锁竞争或执行计划退化。

注意事项

  • 超时后事务已中止,必须回滚并重试整个工作单元,不能在同一连接上继续执行。
  • statement_timeout 无法中断已进入提交阶段或同步复制等待的操作。
  • 它限制单条语句,不限制一个请求内多次查询的累计耗时,仍需请求级超时配合。
  • 批处理任务应使用独立角色,不要为了批处理而放宽 API 角色的默认值。