Lock:tuple - Amazon Aurora

Lock:tuple

在后端进程等待获取元组锁定时,会发生 Lock:tuple 事件。

支持的引擎版本

Aurora PostgreSQL 的所有版本均支持此等待事件信息。

上下文

事件 Lock:tuple 表示一个后端正在等待获取元组上的锁定,而另一个后端在同一个元组上保持冲突锁定。下表说明了会话生成 Lock:tuple 事件的场景。

Time

会话 1

会话 2

会话 3

t1

开始事务。

t2

更新第 1 行。

t3

更新第 1 行。会话获取元组上的独占锁定,然后等待会话 1 通过提交或回滚来释放锁。

t4

更新第 1 行。会话等待会话 2 才能释放元组上的独占锁定。

或者您可以使用基准测试工具 pgbench 来模拟此等待事件。配置大量并发会话以使用自定义 SQL 文件更新表中的同一行。

要了解冲突锁模式的更多信息,请参阅 PostgreSQL 文档中的显式锁定。要了解有关 pgbench 的更多信息,请参阅 PostgreSQL 文档中的 pgbench

等待次数增加的可能原因

当此事件的发生率超过正常(可能表示性能问题)时,典型原因包括以下几点:

  • 大量并发会话试图通过运行 UPDATEDELETE 语句获取相同元组的冲突锁定。

  • 高度并发的会话正在使用 FOR UPDATEFOR 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 中的锁定依赖项信息。要分析过去的 Lock:tuple 事件,请使用 Aurora 函数 aurora_stat_backend_waits

以下示例查询所有会话,并对 tuple 进行筛选,通过 wait_time 进行排序。

--AURORA_STAT_BACKEND_WAITS SELECT a.pid, a.usename, a.app_name, a.current_query, a.current_wait_type, a.current_wait_event, a.current_state, wt.type_name AS wait_type, we.event_name AS wait_event, a.waits, a.wait_time FROM (SELECT pid, usename, left(application_name,16) AS app_name, coalesce(wait_event_type,'CPU') AS current_wait_type, coalesce(wait_event,'CPU') AS current_wait_event, state AS current_state, left(query,80) as current_query, (aurora_stat_backend_waits(pid)).* FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND usename<>'rdsadmin') a NATURAL JOIN aurora_stat_wait_type() wt NATURAL JOIN aurora_stat_wait_event() we WHERE we.event_name = 'tuple' ORDER BY a.wait_time; pid | usename | app_name | current_query | current_wait_type | current_wait_event | current_state | wait_type | wait_event | waits | wait_time -------+---------+----------+------------------------------------------------+-------------------+--------------------+---------------+-----------+------------+-------+----------- 32136 | sys | psql | /*session3*/ update tab set col=1 where col=1; | Lock | tuple | active | Lock | tuple | 1 | 1000018 11999 | sys | psql | /*session4*/ update tab set col=1 where col=1; | Lock | tuple | active | Lock | tuple | 1 | 1000024

在并发性高时降低并发性

Lock:tuple 事件可能会不断发生,特别是在繁忙的工作负载时间。在这种情况下,考虑降低非常繁忙的行的高并发率。通常,只有几个行控制队列或布尔逻辑,这使得这些行非常繁忙。

您可以根据业务需求、应用程序逻辑和工作负载类型使用不同的方法来降低并发性。例如,您可以执行以下操作:

  • 重新设计表和数据逻辑以降低高并发性。

  • 更改应用程序逻辑以降低行级别的高并发性。

  • 使用行级锁定利用和重新设计查询。

  • 使用具有重试操作的 NOWAIT 子句。

  • 考虑使用乐观和混合锁定逻辑并发控制。

  • 考虑更改数据库隔离级别。

排查瓶颈

当出现诸如 CPU 匮乏或 Amazon EBS 带宽的最大使用率的瓶颈时,可能会发生 Lock:tuple。要减少瓶颈,请考虑以下方法:

  • 纵向扩展您的实例类类型。

  • 优化资源密集型查询。

  • 更改应用程序逻辑。

  • 存档很少访问的数据。