Lock:tuple
在后端进程等待获取元组锁定时,会发生 Lock:tuple
事件。
支持的引擎版本
RDS for PostgreSQL 的所有版本均支持此等待事件信息。
上下文
事件 Lock:tuple
表示一个后端正在等待获取元组上的锁定,而另一个后端在同一个元组上保持冲突锁定。下表说明了会话生成 Lock:tuple
事件的场景。
Time |
会话 1 |
会话 2 |
会话 3 |
---|---|---|---|
t1 |
开始事务。 |
||
t2 |
更新第 1 行。 |
||
t3 |
更新第 1 行。会话获取元组上的独占锁定,然后等待会话 1 通过提交或回滚来释放锁。 |
||
t4 |
更新第 1 行。会话等待会话 2 才能释放元组上的独占锁定。 |
或者您可以使用基准测试工具 pgbench
来模拟此等待事件。配置大量并发会话以使用自定义 SQL 文件更新表中的同一行。
要了解冲突锁模式的更多信息,请参阅 PostgreSQL 文档中的显式锁定pgbench
的更多信息,请参阅 PostgreSQL 文档中的 pgbench
等待次数增加的可能原因
当此事件的发生率超过正常(可能表示性能问题)时,典型原因包括以下几点:
-
大量并发会话试图通过运行
UPDATE
或DELETE
语句获取相同元组的冲突锁定。 -
高度并发的会话正在使用
FOR UPDATE
或FOR NO KEY UPDATE
锁定模式运行SELECT
语句。 -
各种因素促使应用程序或连接池打开更多会话以执行相同的操作。由于新会话正在尝试修改相同的行,数据库负载可能会激增,
Lock:tuple
可以出现。
有关更多信息,请参阅 PostgreSQL 文档中的行级锁定
操作
根据等待事件的原因,我们建议采取不同的操作。
调查应用程序逻辑
了解阻止器会话是否已经处于 idle in
transaction
状态很长一段时间。如果是这样,请考虑结束阻止器会话,作为短期解决方案。您可以使用 pg_terminate_backend
函数。有关此函数的更多信息,请参阅 PostgreSQL 文档中的服务器信号函数
要获得长期解决方案,请执行以下操作:
-
调整应用程序逻辑。
-
使用
idle_in_transaction_session_timeout
参数。此参数可结束空闲时间超过指定时间的已打开事务的任何会话。有关更多信息,请参阅 PostgreSQL 文档中的客户端连接原定设置。 -
尽可能多地使用自动提交。有关更多信息,请参阅 PostgreSQL 文档中的 SET AUTOCOMMIT
。
查找阻止器会话
在 Lock:tuple
等待事件发生时,通过找出哪些锁相互依赖来识别阻止器和已阻止的会话。有关更多信息,请参阅 PostgreSQL wiki 中的锁定依赖项信息
以下示例查询所有会话,并对 tuple
进行筛选,通过 wait_time
进行排序。
SELECT blocked_locks.pid AS blocked_pid, blocking_locks.pid AS blocking_pid, blocked_activity.usename AS blocked_user, blocking_activity.usename AS blocking_user, now() - blocked_activity.xact_start AS blocked_transaction_duration, now() - blocking_activity.xact_start AS blocking_transaction_duration, concat(blocked_activity.wait_event_type,':',blocked_activity.wait_event) AS blocked_wait_event, concat(blocking_activity.wait_event_type,':',blocking_activity.wait_event) AS blocking_wait_event, blocked_activity.state AS blocked_state, blocking_activity.state AS blocking_state, blocked_locks.locktype AS blocked_locktype, blocking_locks.locktype AS blocking_locktype, blocked_activity.query AS blocked_statement, blocking_activity.query AS blocking_statement FROM pg_catalog.pg_locks blocked_locks JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid AND blocking_locks.pid != blocked_locks.pid JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid WHERE NOT blocked_locks.GRANTED;
在并发性高时降低并发性
Lock:tuple
事件可能会不断发生,特别是在繁忙的工作负载时间。在这种情况下,考虑降低非常繁忙的行的高并发率。通常,只有几个行控制队列或布尔逻辑,这使得这些行非常繁忙。
您可以根据业务需求、应用程序逻辑和工作负载类型使用不同的方法来降低并发性。例如,您可以执行以下操作:
-
重新设计表和数据逻辑以降低高并发性。
-
更改应用程序逻辑以降低行级别的高并发性。
-
使用行级锁定利用和重新设计查询。
-
使用具有重试操作的
NOWAIT
子句。 -
考虑使用乐观和混合锁定逻辑并发控制。
-
考虑更改数据库隔离级别。
排查瓶颈
当出现诸如 CPU 匮乏或 Amazon EBS 带宽的最大使用率的瓶颈时,可能会发生 Lock:tuple
。要减少瓶颈,请考虑以下方法:
-
纵向扩展您的实例类类型。
-
优化资源密集型查询。
-
更改应用程序逻辑。
-
存档很少访问的数据。