PostgreSQL 子事务的使用的确有风险,但是,将使用控制在合理的范围内,做好监控告警也就可以放心了。
子事务过多的风险
如果 PostgreSQL 中的子事务过多,可能会导致一些风险或问题:
• PostgreSQL 子事务问题: 事务 ID 增长
• PostgreSQL 子事务问题: 子事务缓存溢出
建议了解您的系统中是否使用了子事务。如果有使用,也并不意味着需要立即消除它们,这完全取决于您业务场景下的风险程度。
查询 pg_stat_slru 视图
pg_stat_slru 可用于检查 SLRU(简单、最近最少使用)缓存的使用状态。SLRU 是一种特殊的缓冲区缓存,用于存储非用户数据,例如每个事务的状态,以及有关子事务的信息。异步通知也会使用 SLRU。
从 PostgreSQL 13 开始,您可以查看监控视图 pg_stat_slru,指定name = 'Subtrans',并检查返回行中的blks_read是否持续增长。这表明 PostgreSQL 必须读取磁盘页面,因为它需要访问不再缓存的子事务。
您可以使用下面的查询,来检查 PostgreSQL 中子事务的使用情况:
SELECT blks_hit, blks_read, blks_written,
flushes, truncates
FROM pg_stat_slru
WHERE name = 'Subtrans';
调用 pg_stat_get_backend_subxact 函数
pg_stat_get_backend_subxact() 是一个系统函数,返回指定后端 ID 的后端子事务信息。
从 PostgreSQL 16 开始,您可以调用函数pg_stat_get_backend_subxact(integer),来返回后端进程的子事务数量,以及子事务缓存是否溢出。
下面是一个pg_stat_get_backend_subxact()的用法示例,查询了具有非零子事务计数的任何后端的 PID,子事务信息和最近运行的查询:
SELECT pg_stat_get_backend_pid(bid) AS pid,
s.subxact_count,
s.subxact_overflow,
pg_stat_get_backend_activity(bid) AS query
FROM pg_stat_get_backend_idset() AS bid
JOIN LATERAL pg_stat_get_backend_subxact(bid) AS s ON TRUE
WHERE s.subxact_count > 0;
观察等待事件 SubtransSLRU
通常,在pg_subtrans的 SLRU 子系统负载较高期间,PostgreSQL 中将会出现SubtransSLRU和SubtransBuffer等待事件。SLRU(简单、最近最少使用)缓存是一种由磁盘页面支持的机制,用于存储对 PostgreSQL 操作至关重要的各种事务相关信息。
注意:在 PostgreSQL 12 和更早版本中,对应的等待事件称为SubtransControlLock。SubtransControlLock在 PostgreSQL 13 中被重命名为SubtransSLRU。
在检查一行中的一个子事务的可见性时,PostgreSQL 首先会检索其顶层事务 ID,这需要访问 SLRU 缓存,一个由磁盘存储支持的简单 LRU 缓存。随着子事务溢出的快照的数量增加,并发的后端会抢占对 SLRU 缓存的访问,从而导致SubtransSLRU锁和相关的等待事件。此外,在存在长时间运行的事务时,PostgreSQL 需要为较老的子事务,沿着最近的子事务获取到父事务信息。这扩大了 xid 查找的范围,进一步限制了 SLRU 缓存,并导致频繁的磁盘页面加载和磁盘 I/O 操作,这正如SubtransBuffer等待事件所指示的那样。
您可以使用下面的查询,来观察 PostgreSQL 中的等待事件:
SELECT wait_event_type, wait_event
FROM pg_stat_activity
WHERE pid != pg_backend_pid();