选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

百科 / 错误代码 / Class 55 前置状态不满足

55P03 lock_not_available

ERROR 已实测 详解 实测通过

类别
Class 55 前置状态不满足
严重等级
ERROR
条件名
lock_not_available
宏名称
ERRCODE_LOCK_NOT_AVAILABLE
启用版本
8.0
状态
活跃

版本覆盖

速览

55P03 表示在当前等待策略下无法取得请求的锁。固定路径覆盖行锁、关系锁、LOCK TABLElock_timeout,以及无法取得关系锁时跳过对象的维护命令。

含义

行锁调用使用 LockWaitErrorConditionalXactLockTableWait()/ConditionalMultiXactIdWait(),立即报告 could not obtain lock on row in relation "%s"。关系锁和 LOCK TABLE 使用 ConditionalLockRelationOid(),报告关系对象变体。lock_timeout 是另一个 ERROR 路径,报 canceling statement due to lock timeout;它表示等待超过配置的 timeout,并不表示请求使用了 NOWAIT

VACUUM 和 ANALYZE 取得关系锁失败时也使用此 SQLSTATE,但它们的 ereport(elevel) 有意不提升为 ERROR:普通维护报告 WARNING,autovacuum 的 verbose 路径可能使用 LOG。命令会跳过该对象并继续,因此这类 warning 不等于事务已中止。

报文模板

源码里的格式串,不是某一次运行的输出。%s 之类是占位符,实际报文会填入对象名与取值。适用范围一栏是核验时留下的原始英文记录,未经翻译。

主消息 could not obtain lock on row in relation "%s"

来源:src/backend/access/heap/heapam.c @ REL_18_6

主消息 could not obtain lock on relation "%s.%s"

来源:src/backend/catalog/namespace.c @ REL_18_6

主消息 canceling statement due to lock timeout

来源:src/backend/tcop/postgres.c @ REL_18_6

主消息 could not obtain lock on relation "%s"

来源:src/backend/commands/lockcmds.c @ REL_18_6

主消息 skipping vacuum of "%s" --- lock not available

来源:src/backend/commands/vacuum.c @ REL_18_6

主消息 skipping analyze of "%s" --- lock not available

来源:src/backend/commands/vacuum.c @ REL_18_6

诊断

保留完整 primary message、severity、关系或行名、锁模式和语句。检查 pg_stat_activitypg_locks、阻塞 PID、事务时长,以及语句使用的是 NOWAITlock_timeout 还是维护命令。不要把 VACUUM/ANALYZE 跳过对象的 warning 当成 DML 失败。

已接受的行锁案例使用独立 blocker 和 autocommit contender。显式事务中的锁 ERROR 会使 session 进入 INERROR,直到 ROLLBACKROLLBACK TO SAVEPOINT;该案例的 autocommit contender 在失败后保持 IDLE

处理

协调或释放 blocker,或明确选择等待、timeout、skip 策略。显式事务中的 ERROR 后,先回滚整个事务或回到既有 savepoint,再发送 SQL,并核对已提交工作后重放。autocommit 下先改变阻塞或 timeout 条件,再按幂等规则重试语句。维护 WARNING/LOG 只需记录跳过的关系并稍后补做维护;命令没有中止事务时不要执行 ROLLBACK

可复现案例

在一次性实例上执行过的场景。其中 1 个附有可执行 SQL,正文相应小节里给出。

nowait_row_lock PG 10 / 18 有 SQL

前置条件

  • Two runner-owned sessions target one row.
  • The blocker acquires its lock before the NOWAIT statement is sent.

触发

Hold a row lock in one explicit transaction, issue SELECT FOR UPDATE NOWAIT from a second session, then release and retry.

断言

  • SQLSTATE is 55P03 with ERROR severity
  • The diagnostic identifies a row lock that cannot be obtained immediately
  • The blocker is INTRANS and the NOWAIT contender remains IDLE after its error
  • After blocker rollback the same contender updates and reads the row

处置

Use a bounded NOWAIT failure as a signal, release or resolve the blocker, then retry according to the application’s lock policy; this case does not treat sleeping as synchronization.

清理

Close all runner connections and drop the case schema with an owner connection.

实测诊断

选定注册表创建 blocker,在显式事务中持有 SELECT ... FOR UPDATE,并由 autocommit contender 发出 FOR UPDATE NOWAIT。PostgreSQL 18.6 与 10.21 均返回 ERROR / 55P03could not obtain lock on row in relation "nowait_rows";contender 保持 IDLE。blocker 回滚后,同一 contender 将 marker 更新为 repaired 并完成验证。

代表案例

以下 SQL 是带角色说明的注册表片段。setup 只执行一次,blocker 与 contender 必须使用不同会话;片段只覆盖行锁 NOWAIT,不覆盖 lock_timeout 或维护跳过分支。

-- setup(一个维护会话)
CREATE TABLE nowait_rows(id integer PRIMARY KEY, marker text NOT NULL);
INSERT INTO nowait_rows VALUES (1, 'seed');

-- blocker 会话:保持事务打开
BEGIN;
SELECT id FROM nowait_rows WHERE id = 1 FOR UPDATE;

-- contender 会话,开启 autocommit:这里触发 55P03
SELECT id FROM nowait_rows WHERE id = 1 FOR UPDATE NOWAIT;

-- blocker 会话
ROLLBACK;

-- 释放 blocker 后,在 contender 会话中修复并验证
UPDATE nowait_rows SET marker = 'repaired' WHERE id = 1 RETURNING marker;
SELECT marker FROM nowait_rows WHERE id = 1;

该案例只确认行锁机制、ERROR/IDLE 边界和修复步骤,不能据此断言每种锁模式或维护命令有相同 severity。

版本

锁定目录从 8.0.0 记录此条件;固定源码覆盖 PostgreSQL 18.6。已接受的运行观察来自 PostgreSQL 18.6 和 10.21。

来源

证据

断言

每条断言都写明了是怎么核实的,以及它不覆盖什么。这一层是核验时留下的原始英文记录,照原样呈现,未经翻译。

运行记录

目标服务器版本结果覆盖案例
latest 18.6 (Homebrew) passed nowait_row_lock
pg10 10.21 (Debian 10.21-1.pgdg90+1) passed nowait_row_lock

同类错误代码

Class 55 前置状态不满足 下的其他成员。