PostgreSQL 17 新参数 transaction_timeout 介绍
PostgreSQL过去有各种控制语句或会话超时参数,例如:
`statement_timeout`,语句的最长执行时间
`idle_in_transaction_session_timeout`,事务内会话空闲的最长时间
`idle_session_timeout`,会话空闲的最长时间
对于PostgreSQL有一定经验的用户应该都会知道,长事务对于数据库的性能、稳定性影响是非常巨大的。长事务会导致VACUUM无法回收垃圾数据,会导致数据版本持续增加,从而影响数据库的性能。过多的垃圾数据也会占用磁盘空间。长事务本身也会导致事务快照区间变大,可见性判断的成本也会更高,hintbit 也无法设置,任何的增删改查的速度都会因此变慢。如果持续无法进行VACUUM,甚至会导致数据库年龄回卷,直接导致数据库无法继续提供服务。
过去,长事务一直没有有效简单的避免方式,只能通过外部的轮询脚本定时检测并终止超时的长事务。有的时候,长事务呈现出一些特征,才能通过`idle_in_transaction_session_timeout`、`statement_timeout`等进行控制,但是这些参数终究不是控制长事务的最简单和有效的方式。
现在,PostgreSQL 17 引入了新的超时时间:`transaction_timeout`。首先看一下官方文档的介绍:
transaction_timeout (integer)
Terminate any session that spans longer than the specified amount of time in a transaction. The limit applies both to explicit transactions (started with BEGIN) and to an implicitly started transaction corresponding to a single statement. If this value is specified without units, it is taken as milliseconds. A value of zero (the default) disables the timeout.
If transaction_timeout is shorter or equal to idle_in_transaction_session_timeout or statement_timeout then the longer timeout is ignored.
Setting transaction_timeout in postgresql.conf is not recommended because it would affect all sessions.
Note
Prepared transactions are not subject to this timeout.
该参数可以控制最长的事务执行时间,如果超过这个时间,会话就会被终止。默认单位是毫秒,0表示关闭。可以和其他超时参数组合使用,将会取更短的超时值。并不建议将参数设置到配置文件中,因为这将会影响所有会话。更加推荐的做法是在会话级别进行设置。两阶段事务并不在该参数的控制范围内。
PolarDB PG 14 及以上版本也都支持该参数,但参数的名称有所变化,在PolarDB PG 14、15、16中,参数名是`polar_transaction_timeout`,在 PolarDB PG 17 中,参数名为`transaction_timeout`。
下面是一个简单的例子,通过`transaction_timeout`控制长事务的终结:
-- 设置超时时间为1秒
SET transaction_timeout = '1s';
-- 开启一个长事务
BEGIN;
-- sleep 3s
SELECT pg_sleep(3);
-- 此时可以观察到会话被及时地终止
可以看到这个参数可以非常简单地控制活跃会话产生的长事务。另外还有些实现上的细节值得说明下。
前面提到,当事务超时,被杀掉的是会话,整个连接会被中断掉。为什么不能像`statement_timeout`一样,只结束执行中的长事务语句呢?这是因为长事务不仅只有执行语句的状态,还有 idle 的状态,此时`transaction_timeout`就会像`idle_in_transaction_session_timeout`一样工作,直接将会话完全结束掉。再深入些的话,就是处于 idle 状态时,会话进程不响应 SIGINT 信号,无法通过普通的报错将超时告知给会话。因此,这个功能只能将超时的会话完全结束。
前面还提到这个参数不支持两阶段事务,这是因为两阶段事务和普通事务有很大的不同。两阶段事务只有活跃的事务,没有活跃的会话。所以在杀掉事务上的实现会和当前的实现有很大不同,为了 patch 的简洁,社区暂时没有引入两阶段事务的支持。日后社区可能会实现两阶段事务的自动清理功能,可能需要autovacuum worker之类的守护进程完成。




