大事务是指影响数据库性能、存储、并发的事务,而不是单纯指 SQL 长语句,大事务拦截可根据实际需要,结合pg_stat_activity, pg_stat_statements等监控工具、合理设置超时、自动清理机制,防止或降低大事务对系统稳定性的影响。
以 PostgreSQL 17(CentOS 7.9)为例,上实现大事务拦截,有以下几种方法:
方案1:使用 pg_stat_activity + 触发 pg_terminate_backend
原理:定期监控正在执行的事务,发现大事务(如占用大量资源、运行时间过长)后终止它。
步骤:
-
创建一个定时任务,定期扫描 pg_stat_activity 视图。
-
设定阈值,例如:
-
事务执行时间超过N秒。
-
事务占用 work_mem 或 temp_file 过大的话(可结合 pg_stat_statements)。
-
-
终止超过阈值的事务。
示例脚本(每分钟执行一次,终止运行超过 10 分钟的事务):
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'active'
AND now() - query_start > interval '10 minutes'
AND pid <> pg_backend_pid();
复制
使用cron 定时任务自动部署 :
*/1 * * * * postgres psql -d yourdb -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '10 minutes' AND pid <> pg_backend_pid();"
复制
方案2:使用 pg_stat_statements 监控资源消耗
pg_stat_statements 可用于检测高资源消耗的 SQL。
编辑 postgresql.conf ,启用 pg_stat_statements:
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all
pg_stat_statements.max = 10000
复制
重启PostgreSQL:
systemctl restart postgresql-17
复制
创建扩展:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
复制
查询高消耗 SQL
SELECT query, calls, total_time, rows, mean_exec_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;
复制
可以结合 pg_terminate_backend 终止异常 SQL。
方案3:使用 statement_timeout 限制 SQL 运行时间
statement_timeout 设定单条 SQL 最大执行时间,超时自动终止。
修改 postgresql.conf(全局生效):
statement_timeout = ‘10min’
或对特定用户/会话生效:
ALTER ROLE someuser SET statement_timeout = '5min';
SET statement_timeout = '5min';
复制
方案4:使用 pg_wal 监控 WAL 大事务
PostgreSQL WAL 记录大事务,可结合 pg_stat_bgwriter、pg_stat_database 监测 WAL 写入速率。
监测 WAL 写入过快的数据库
SELECT datname, blks_written, buffers_alloc
FROM pg_stat_database;
复制
当 blks_written 快速增长,可能是大事务。
拦截大事务: 配合 pg_terminate_backend 终止长时间占用 WAL 的事务:
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'active'
AND now() - xact_start > interval '10 minutes';
复制
方案5:使用 event trigger 拦截 BEGIN 事务
可以通过事件触发器(Event Trigger)防止特定事务执行。
步骤:
- 启用pg_event_trigger:
CREATE FUNCTION block_large_transactions()
RETURNS event_trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF (SELECT count(*) FROM pg_stat_activity WHERE state = 'active' AND now() - xact_start > interval '10 minutes') > 0 THEN
RAISE EXCEPTION 'Transaction too large!';
END IF;
END;
$$;
复制
- 绑定到BEGIN事件:
CREATE EVENT TRIGGER prevent_large_tx
ON ddl_command_start
EXECUTE FUNCTION block_large_transactions();
复制
方案6:使用外部工具(pgbouncer / pg_kill)
- pgbouncer:限制最大事务时长,超时自动断开连接。
- pg_kill:第三方工具,可自动终止 PostgreSQL 长事务。
总结
- 轻量级方案:statement_timeout(简单易用)
- 自动化拦截:pg_stat_activity + pg_terminate_backend
- SQL 级分析:pg_stat_statements
- 防止大 WAL 事务:监控 pg_wal
- DDL 级防护:event trigger
您可以根据不同场景,使用单独的方案或组合多个方案。