一、问题描述
在使用pg_repack这个工具做数据库的表膨胀清理过程中,经常会遇到类似这样的警告:
这里的警告表明在膨胀治理的时候,此表遇到了事务阻塞,而此时我们有三种选择,第一个选择是等待该事务结束,第二个选择是主动结束该事务,第三个选择是利用pg数据库的自身机制自动结束该事务;只有事务阻塞的情况解决了,才能够顺利的完成表膨胀的治理
查询上面警告的进程,可以看到该进程的连接状态是idle in transaction
二、数据库进程和数据库连接状态
那么,在解决此问题前,先来了解一下数据库连接的几种状态。
pg_stat_activity 是一张postgresql数据库内的系统视图,它的每一行都表示一个系统进程,显示与当前会话的活动进程的一些信息,比如当前会话的状态和查询等。
在平时查询pg_stat_activity这个视图的时候,每一行包含了一个进程的相关信息,包含当前正在执行的SQL,或者会话的状态等等,state字段表示当前进程的状态。
在PostgreSQL数据库里,其实代码里总共定义了7种BackendState,但是最终给我们展现在pg_stat_activity里显示的只有6种,这个不显示的STATE_UNDEFINED是PostgreSQL中定义的一个连接状态。它表示客户端连接到服务器,但服务器无法确定连接的状态,这里这个特殊的状态本文就不过多解读了,并不是本文的重点它的state字段表示当前进程的状态,一共有六种:
1、Active(活动): 进程正在执行某个语句,处于活跃状态
2、Idle(空闲): 进程正在等待客户端的指令
3、idle in transaction(事务空闲):进程开启了事务,但当前没有提交任何语句,这里的事务可以是查询语句,也可以是插入语句,总之,DML语句和DDL语句都可以
4、idle in transaction (aborted)(事务空闲-退出):进程开启了事务,但当前没有提交任何语句。并且事务中的一个语句报错退出。(一般整个事物回滚后的状态)
除了事务中声明一个错误外,其余情况与idle in transaction相同
5、fastpath function call(快速通道函数调用): 后台正在执行某个快速通道函数
6、Disabled(禁用): 如果后台禁用track_activities,则报告这个状态
这里主要介绍下idle in transaction,它是一种特殊的进程状态,也就是上面图片里的示例,它表示进程里的一个事务已经开始,但尚未完成。当一个事务处于idle in transaction状态时,它可以接受新的查询,但不能提交或回滚。这种状态通常是由于客户端应用程序在发送查询之后没有发送提交或回滚指令而导致的。可能在业务应用代码中忘记关闭已开启的事务,或者系统中存在僵死进程等。
idle in transaction进程过多的危害:
数据库里长时间存在idle in transaction状态的进程,会严重影响数据库的性能,因为它会阻止其他事务的执行,从而影响数据库的性能。此外,如果一个事务处于idle in transaction状态太长时间,它会阻止VACUUM进程回收空间,当然了,也会阻止pg_repack回收磁盘空间,进而造成表数据膨胀,会导致事务ID wraparound,甚至严重可能会占用大量的内存,从而导致数据库崩溃。
而数据库的一个比较重要的原则是 快进快出,该原则同样适用于vacuum,pg_repack 清理表膨胀的时候遇到事务阻塞的情况,也就是说,事务能够尽快开始并且尽快的结束是最好的情况
三、
手动清理长时间挂起的事务
第一个SQL语句是查询事务阻塞的进程pid,第二个SQL语句是结束查询到的进程pid
很明显的,手动清理阻塞事务是比较繁琐的,通常需要清理的pid很多,工作量很大,此种方式十分不推荐
SELECT pid, pg_stat_get_backend_activity(pid) AS query
FROM pg_stat_activity
WHERE state = 'idle in transaction';
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction' AND pid = <进程ID>;
将 <进程ID> 替换为你要终止的进程的实际 ID。这将终止与指定进程关联的数据库事务。
请注意,终止一个进程将导致未提交的事务回滚,并释放相关的资源。在执行此操作之前,请确保你了解可能带来的影响。
四、
postgresql数据库自动清理长时间挂起的事务
PostgreSQL 9.6版本开始支持了idle_in_transaction_session_timeout参数,也就是上面我们所说的第三种选择---自动查杀idle_in_transaction_session_timeout进程,这个参数可以自动查杀超过指定时间的 idle in transaction 空闲事务连接,用于清理应用代码中忘记关闭已开启的事务,或者系统中存在僵死进程等。(在postgresql数据库的主配置文件内修改此参数,默认是0,关闭状态)
那么,这个参数到底如何配置呢?该参数是毫秒,比如设置idle_in_transaction_session_timeout=20000 意思就是20秒以上idle_in_transaction_session状态的进程postgresql数据库会给你自动清理掉,🆗,如果你的数据库读写性能比较差,一个正常发起的事务一般都会超过20秒,那么,将此参数增大,比如增大到60000,也就是60秒就可以了。通常可以问开发,确认自己数据库的事务执行提交时间。
需要注意的是,修改idle_in_transaction_session_timeout参数只需要重载数据库配置文件(也就是在命令行执行 selec pg_reload_conf(); 这个函数),并不需要重启数据库服务,而且它不会影响idle状态的事物。
据观察,idle_in_transaction_session_timeout参数设置后,不管设置多少,至少pg_repack 在清理表膨胀的时候再也没有遇到因事务阻塞而需要等待的情况了