Skip to content

[Bug] PolarDB 15.8.2.0 CREATE INDEX 导致 XactLockTableWait 无限等待 #631

Description

@yueludanfeng

1. 环境信息

项目
数据库版本 PostgreSQL 15.8 (PolarDB 15.8.2.0 build 71f39e5) on x86_64-linux-gnu
操作系统 openEuler 22.03 (LTS-SP4), kernel 5.10.0-216.0.0.115.oe2203sp4.x86_64
CPU Intel Xeon Processor (Cascadelake)
内存 7.3Gi total, 5.0Gi used
数据目录 /opt/common-database0/data/pgdata
配置文件 /opt/common-database0/data/pgdata/postgresql.conf
shared_buffers 262144 (2GB)
max_connections 1200
synchronous_commit off
wal_level replica
max_wal_size 256
checkpoint_timeout 300
pg_is_in_recovery false (主节点)
数据库大小 8621 kB

2. 问题描述

在 PL/pgSQL 升级函数(长事务)中执行 CREATE INDEX IF NOT EXISTS idx_tbl_res_tree_res_id ON public.tbl_res_tree (res_id) 时,CREATE INDEX 卡在 building index: scanning table 阶段超过 2.5 小时,无法推进。同时阻塞了 9 个业务查询(4 个 INSERT/UPDATE tbl_res_tree + 5 个 INSERT tbl_operlog)。

核心异常:目标表 tbl_res_tree 仅有 4 行数据(8KB),扫描 1 个 block 瞬间完成(blocks_done=1, blocks_total=1),但 CREATE INDEX 无法进入 sorting 阶段。

3. GDB 调用堆栈(核心证据)

#0  0x00007fc3907022f9 in select () from /usr/lib64/libc.so.6
#1  0x00000000009c3629 in pg_usleep (microsec=microsec@entry=1000) at pgsleep.c:56
#2  0x00000000008398c8 in XactLockTableWait (xid=xid@entry=84466, rel=rel@entry=0x7fc303538f10, 
    ctid=ctid@entry=0x34c9f74, oper=oper@entry=XLTW_InsertIndexUnique) at lmgr.c:719
#3  0x0000000000561f41 in heapam_index_build_range_scan (heapRelation=0x7fc303538f10, 
    indexRelation=0x7fc303539830, indexInfo=0x335fe68, allow_sync=<optimized out>, 
    anyvisible=false, progress=true, start_blockno=0, numblocks=4294967295, 
    callback=0x57bed0 <_bt_build_callback>, callback_state=0x7ffd8cd27060, scan=0x34c9f18) 
    at heapam_handler.c:1485
#4  0x000000000057d897 in table_index_build_scan (...) at tableam.h:1756
#5  0x000000000057d897 in _bt_spools_heapscan (...) at nbtsort.c:497
#6  0x000000000057d897 in btbuild (...) at nbtsort.c:343
#7  0x0000000000604cb8 in index_build (...) at index.c:3032
#8  0x0000000000605fa1 in index_create (indexRelationName="idx_tbl_res_tree_res_id", 
    indexRelationId=34127) at index.c:1254
#9  0x000000000069239e in DefineIndex (relationId=24034, 
    queryString="CREATE INDEX IF NOT EXISTS idx_tbl_res_tree_res_id ON public.tbl_res_tree (res_id)") 
    at indexcmds.c:1161

关键卡点XactLockTableWait(xid=84466, oper=XLTW_InsertIndexUnique)pg_usleep(1000) 中无限循环,1ms 间隔轮询等待事务 84466 结束。

4. 异常事务 ID 状态

检查项 结果 含义
PID 457501 的 backend_xid 14290 自身事务 ID
GDB 中的等待目标 xid 84466 等待的事务 ID
txid_status(84466) ERROR: transaction ID 84466 is in the future 事务 84466 无法解析!
当前 txid_current() 14806 84466 > 14806
pg_lockstransactionid=84466 0 行 无进程持有该事务 ID
pg_stat_activitybackend_xid=84466 0 行 无活跃事务使用该 ID

事务 84466 既不是当前活跃事务,也查不到 commit/abort 记录,系统认为它是"未来事务"。这是导致 XactLockTableWait 无限等待的根本原因。

5. pg_stat_progress_create_index 状态

字段 异常点
pid 457501 -
relid 24034 (tbl_res_tree) -
command CREATE INDEX -
phase building index: scanning table 扫描已完成但阶段未推进
blocks_total 1 -
blocks_done 1 ✅ 扫描完成
tuples_total 0 ❌ 未统计
tuples_done 0 ❌ 未统计
lockers_total 0 无显式锁持有者

6. 锁等待链

PID 457501 (CREATE INDEX):
  持有: tbl_res_tree ShareLock
  持有: OID 34120, 34123, 34124, 34125, 34127 的 AccessExclusiveLock(新创建对象)
  持有: OID 34120, 34123 的 ShareLock
  持有: transactionid 14290 ExclusiveLock
  卡在: XactLockTableWait(xid=84466) 无限循环

被阻塞的业务进程(共 9 个):
  PID 458699: INSERT tbl_res_tree → 等 tbl_res_tree RowExclusiveLock (2h28m)
  PID 460397: INSERT tbl_res_tree → 等 tbl_res_tree RowExclusiveLock (2h28m)
  PID 460396: INSERT tbl_res_tree → 等 tbl_res_tree RowExclusiveLock (2h28m)
  PID 460399: UPDATE tbl_res_tree → 等 tbl_res_tree RowExclusiveLock (2h28m)
  PID 460402: INSERT tbl_operlog → 等 tbl_operlog (2h18m)
  PID 460618: INSERT tbl_operlog → 等 tbl_operlog (2h26m)
  PID 460814: INSERT tbl_operlog → 等 tbl_operlog (2h25m)
  PID 461196: INSERT tbl_operlog → 等 tbl_operlog (2h15m)
  PID 461384: INSERT tbl_operlog → 等 tbl_operlog (2h14m)

7. 目标表信息

项目
表名 tbl_res_tree
行数 4
表大小 8192 bytes
索引大小 32 kB
现有索引 pk_tbl_res_tree, idx_tbl_res_tree_phy_res_code_phy_domain_code_phy_res_type
待创建索引 idx_tbl_res_tree_res_id (res_id) — 创建中卡住
relpages 0
reltuples -1
dead_tuples 0
live_tuples 0
xmin/xmax 4 行 xmax 均为 0

8. OID 34120-34127 异常

OID 34120, 34123, 34124, 34125, 34127 被 PID 457501 持有 AccessExclusiveLock 和 ShareLock,但 pg_class 中查不到这些 OID 对应的对象:

SELECT oid, relname, relkind FROM pg_class WHERE oid IN (34120, 34123, 34124, 34125, 34127);
-- 返回 0 行

说明这些对象正在创建过程中(事务未提交),是 func_AB90441() 的 CREATE TABLE tbl_res_spec 及其相关索引/类型。

9. 升级函数调用链

-- 顶层函数
SELECT * FROM func_VMSV200R001B06_Version_1_0_10();

-- 内部调用顺序:
perform func_AB90836();  -- ✅ 已完成(INSERT tbl_user_role_relation)
perform func_AB90441();  -- ✅ 已完成(CREATE TABLE tbl_res_spec,事务未提交)
perform func_AB89542();  -- 🔴 卡在这里
  → CREATE INDEX IF NOT EXISTS idx_tbl_res_tree_res_id ON public.tbl_res_tree (res_id);
perform func_AB95348();  -- ⏳ 未执行

当前 tbl_version.inner_version = 1_0_8,说明升级事务尚未提交。

10. 触发条件分析

必要条件:
1. PolarDB 15.8.2.0
2. 长事务中执行 CREATE INDEX(PL/pgSQL 函数内)
3. 同一事务中先执行了其他 DDL(CREATE TABLE),导致新 OID 分配
4. 并发业务进程对同一张表执行 INSERT/UPDATE
5. heapam_index_build_range_scan 扫描时遇到 xmax=84466 的行
   → 84466 是"幽灵事务 ID":不在活跃事务列表、查不到 commit/abort 状态
   → XactLockTableWait 进入无限 pg_usleep(1000) 循环

推测根因:
PolarDB 的全局事务 ID 映射与数据页中存储的 xmax 不一致,
导致 TransactionIdIsCurrentTransactionId()、TransactionIdDidCommit()、
TransactionIdDidAbort() 对 xid=84466 均返回 false,
XactLockTableWait 永远无法退出等待循环。

11. strace 输出(补充证据)

pselect6(0, NULL, NULL, NULL, {tv_sec=0, tv_nsec=1000000}, NULL) = 0 (Timeout)

持续以 1ms 间隔轮询,对应 XactLockTableWaitpg_usleep(1000) 的无限循环。

12. 期望

  1. 确认 PolarDB 15.8.2.0 是否存在此已知 bug(XactLockTableWait 对"幽灵事务 ID"无限等待)
  2. 确认此 bug 在哪个版本修复
  3. 提供临时规避方案(除 pg_terminate_backend 外是否有更优雅的方式)
  4. 确认升级到 PolarDB 15.18.5.0 是否能解决此问题

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions