长时间运行 / 未结束的事务
长时间运行或未结束的事务正在累积锁、undo 和膨胀(bloat)。我们找出问题事务,确定其类型(idle in transaction、长批处理、影响),并安全地解决它。
最早的未结束事务是哪一个?
按开始时间列出未结束的事务。最早的那个通常就是问题的根源。检查其状态:是正在活跃运行,还是处于等待提交的 'idle in transaction' 状态?
SELECT s.sid, s.serial#, s.username, s.status, t.start_time, t.used_ublk*8/1024 AS undo_mb
FROM v$transaction t JOIN v$session s ON s.taddr=t.addr
ORDER BY t.start_time;- idle in transaction(忘记提交,处于空闲)
- 活跃但运行时间极长的批处理/DML
- 事务持续期间正在累积锁/膨胀/undo
为什么会出现 idle in transaction?
会话打开了事务,但处于空闲状态,没有运行查询;也就是说,忘记了 commit/rollback。这会不必要地占用锁和 undo。是修复应用就足够,还是需要紧急终止它?
SELECT pid, now()-state_change AS idle_age, left(query,80) AS last_query
FROM pg_stat_activity WHERE state='idle in transaction' ORDER BY state_change;- 应用/连接池没有提交(永久修复)
- 我需要立即终止它
活跃的长事务在做什么?
该会话确实在运行(active)。它是单个巨型 DML/报表,还是一个正在让其他会话等待、必须紧急切断的阻塞者?
SELECT s.sid, s.serial#, sq.sql_text, s.last_call_et AS running_secs
FROM v$session s JOIN v$sql sq ON sq.sql_id=s.sql_id
WHERE s.status='ACTIVE' AND s.type='USER' ORDER BY s.last_call_et DESC;- 单个巨型 DML/报表;需要拆分
- 它正在让其他会话等待;紧急终止
长事务正在累积什么?
确定长事务的副作用:主要是导致其他会话等待的锁积压,还是因持有旧的行版本而阻碍清理(VACUUM/undo)所造成的膨胀?
SELECT pid, pg_blocking_pids(pid) AS blocked_by FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid))>0;
SELECT max(age(backend_xmin)) AS max_xmin_age FROM pg_stat_activity WHERE backend_xmin IS NOT NULL;- 锁积压正在让其他会话等待
- 旧版本堆积正在阻碍清理(undo/膨胀)
根本原因:应用没有提交(idle in transaction)
连接池或应用打开事务后,不提交/回滚就让连接保持空闲。这会不必要地占用锁和 undo。
解决方案:安全地终止问题会话
如果一个阻塞/长时间运行的会话正在让其他会话等待,必须以受控方式终止它。终止会触发回滚,可能需要空间/时间;需谨慎。
根本原因:单个巨型 DML/报表
在单个事务中处理数百万行的 DML,或运行时间极长的报表,会在整个持续期间累积锁和 undo/膨胀。
根本原因:锁积压队列
由于持有的锁,长事务在其身后形成了等待队列;许多会话正在等待同一资源。
根本原因:旧版本堆积(undo/膨胀)
长时间运行的事务阻止了对其之前的旧行版本的清理。在 Postgres 中表现为表/索引膨胀,在 Oracle 中表现为 undo 压力。