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_locks 中 transactionid=84466 |
0 行 |
无进程持有该事务 ID |
pg_stat_activity 中 backend_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 间隔轮询,对应 XactLockTableWait → pg_usleep(1000) 的无限循环。
12. 期望
- 确认 PolarDB 15.8.2.0 是否存在此已知 bug(XactLockTableWait 对"幽灵事务 ID"无限等待)
- 确认此 bug 在哪个版本修复
- 提供临时规避方案(除
pg_terminate_backend 外是否有更优雅的方式)
- 确认升级到 PolarDB 15.18.5.0 是否能解决此问题
1. 环境信息
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 调用堆栈(核心证据)
关键卡点:
XactLockTableWait(xid=84466, oper=XLTW_InsertIndexUnique)在pg_usleep(1000)中无限循环,1ms 间隔轮询等待事务 84466 结束。4. 异常事务 ID 状态
backend_xidxidtxid_status(84466)txid_current()pg_locks中transactionid=84466pg_stat_activity中backend_xid=84466事务 84466 既不是当前活跃事务,也查不到 commit/abort 记录,系统认为它是"未来事务"。这是导致
XactLockTableWait无限等待的根本原因。5. pg_stat_progress_create_index 状态
6. 锁等待链
7. 目标表信息
8. OID 34120-34127 异常
OID 34120, 34123, 34124, 34125, 34127 被 PID 457501 持有 AccessExclusiveLock 和 ShareLock,但
pg_class中查不到这些 OID 对应的对象:说明这些对象正在创建过程中(事务未提交),是 func_AB90441() 的 CREATE TABLE tbl_res_spec 及其相关索引/类型。
9. 升级函数调用链
当前
tbl_version.inner_version = 1_0_8,说明升级事务尚未提交。10. 触发条件分析
11. strace 输出(补充证据)
持续以 1ms 间隔轮询,对应
XactLockTableWait→pg_usleep(1000)的无限循环。12. 期望
pg_terminate_backend外是否有更优雅的方式)