3.2
|
| 字段 | 含义 |
|---|---|
ID |
OBServer 内部 session id(应用层 session,可 KILL) |
PROXY_SESSID |
OBProxy 会话 ID |
TRANS_ID |
当前事务 ID(与 GV$OB_LOCKS 关联的桥梁) |
TRANS_STATE |
ACTIVE / IMPLICIT_ACTIVE / ROLLBACK |
SQL_ID |
当前 SQL |
INFO |
SQL 文本 |
STATE |
ACTIVE / SLEEP |
gv$active_session_history| 字段 | 含义 |
|---|---|
session_id |
事务调度器 session id(不是 processlist 的 ID) |
BLOCKING_SESSION_ID |
阻塞者的事务调度器 session id |
SESSION_STATE |
WAITING / ON CPU |
event |
row lock wait |
GV$OB_TRANSACTION_PARTICIPANTS / GV$OB_TRANSACTION_SCHEDULERS| 字段 | 含义 |
|---|---|
TX_ID |
事务 ID |
SESSION_ID |
事务调度器 session id |
ROLE |
LEADER / FOLLOWER |
STATE |
ACTIVE / COMMITTED / ABORTED |
LAST_REQUEST_TIME |
最后一次操作时间(判断长事务) |
TX_EXPIRED_TIME |
事务过期时间 |
CTX_CREATE_TIME |
事务上下文创建时间 |
ACTION |
START / COMMIT / ABORT |
dba_ob_table_locations(辅助定位)| 字段 | 含义 |
|---|---|
TABLET_ID |
tablet 编号(与 GV$OB_LOCKS.ID3 中的 TABLET_ID 对应) |
ZONE |
所在 Zone |
SVR_IP / SVR_PORT |
节点 IP/端口 |
ROLE |
副本角色(LEADER / FOLLOWER) |
DATABASE_NAME / TABLE_NAME |
数据库名/表名 |
用途:从 GV$OB_LOCKS.ID3 解析出 tablet_id 后,反查此表得到表/分区所在位置,便于跨节点排查。
| 层级 | 字段来源 | 含义 | 案例值 |
|---|---|---|---|
| 客户端层(MySQL 模式) | connection_id() |
业务连接 session(MySQL 协议) | 3222040188 |
| 客户端层(Oracle 模式) | USERENV('SID') |
业务连接 session(Oracle 协议) | 284713 |
| OBProxy 层 | PROXY_SESSID |
OBProxy 内部会话 ID | 12399… |
| OBServer 层 | gv$ob_processlist.ID |
OBServer 内部 session(可 KILL) | 284713 / 3222040188 |
| 事务调度器层 | GV$OB_LOCKS.SESSION_ID |
事务调度器 session(不可 KILL) | 3221692919 |
| 历史层(ASH) | gv$active_session_history.session_id |
采样的 session | 3221692919 |
| 关联 | 方法 | 案例验证 |
|---|---|---|
connection_id() / USERENV('SID') ↔ gv$ob_processlist.ID |
直接相同 | 284713 = 284713 |
gv$ob_processlist.ID ↔ gv$ob_processlist.TRANS_ID |
同一记录字段 | 284713 持有 31007974 |
GV$OB_LOCKS.SESSION_ID ↔ gv$ob_processlist.ID |
不能直接 JOIN | 3221692919 ≠ 284713(无映射) |
GV$OB_LOCKS.ID1(等待者行)↔ gv$ob_processlist.TRANS_ID |
通过 TRANS_ID 关联 |
31007974 = 31007974 |
gv$active_session_history.BLOCKING_SESSION_ID |
事务调度器 session | 3221692919(要去事务视图找) |
路径 A:GV$OB_LOCKS → gv$ob_processlist(推荐)
GV$OB_LOCKS.BLOCK=1 的等待者行
↓ 提取 ID1(持有者 TRANS_ID = 31007974)
gv$ob_processlist WHERE TRANS_ID = 31007974
↓ 得到应用层 ID = 284713
KILL 284713; -- MySQL 模式
ALTER SYSTEM KILL SESSION '284713,@192.168.56.213:2882' TENANT = orcl; -- Oracle 模式
路径 B:GV$OB_LOCKS → GV$OB_TRANSACTION_PARTICIPANTS → gv$ob_processlist(参考官方文档路径)
GV$OB_LOCKS.BLOCK=1 的等待者行
↓ 提取 ID1(持有者 TRANS_ID = 31007974)
GV$OB_TRANSACTION_PARTICIPANTS WHERE TX_ID = 31007974
↓ 得到事务调度器 session_id = 3221692919
gv$ob_processlist WHERE ID = 3221692919
↓ (本环境验证返回 Empty set,路径 B 在本环境可能不成立)
⚠️ 路径 B 的风险:参考官方文档走的是这条路径,但在本案例中验证 gv$ob_processlist WHERE ID = 3221692919 返回 Empty set——这是因为本环境中 GV$OB_LOCKS.SESSION_ID 是事务调度器 ID,不直接对应 gv$ob_processlist.ID。应以本环境实际行为为准,优先使用路径 A(TRANS_ID 关联)。
无论是阻塞者还是等待者,排查过程中可能耗时较长,先在两个 session 都设置超时时间,避免排查过程中连接被自动断开:
MySQL 模式:
SET ob_query_timeout = 3600000000; -- SQL 最大执行时间(3600s)
SET ob_trx_timeout = 3600000000; -- 事务超时(3600s)
SET ob_trx_idle_timeout = 3600000000; -- 事务空闲超时(3600s)
Oracle 模式:
ALTER SESSION SET ob_query_timeout = 3600000000;
ALTER SESSION SET ob_trx_timeout = 3600000000;
ALTER SESSION SET ob_trx_idle_timeout = 3600000000;
-- 关键过滤:BLOCK=1(等待者)
SELECT SVR_IP, SVR_PORT, TENANT_ID, TRANS_ID, SESSION_ID, TYPE,
ID1, ID2, ID3, LMODE, REQUEST,
ROUND(CTIME / 1000000) AS CTIME_S, BLOCK
FROM GV$OB_LOCKS
WHERE BLOCK = 1 -- 等待者
AND CTIME > 10000000 -- 等待超过 10 秒(CTIME 单位是微秒)
ORDER BY CTIME DESC;
记下:
TRANS_IDID1(持有者 TRANS_ID)ID2(持有者事务调度器 session)ID3(解析 TABLET_ID-ROWKEY)-- 从 ID3 解析出 tablet_id(去掉 ROWKEY 部分),反查表/分区位置
SELECT zone, svr_ip, svr_port, role, tablet_id, database_name, table_name
FROM dba_ob_table_locations
WHERE tablet_id = <TABLET_ID> -- 从 ID3 解析
ORDER BY zone, svr_ip, svr_port;
SELECT svr_ip, svr_port, ID, user, tenant, time, total_time,
state, PROXY_SESSID, SQL_ID, TRANS_ID, TRANS_STATE, INFO
FROM gv$ob_processlist
WHERE TRANS_ID = '<持有者TRANS_ID>'; -- 本案:31007974
SELECT SVR_IP, session_id, SESSION_STATE, event, BLOCKING_SESSION_ID
FROM gv$active_session_history
WHERE sql_id = '<等待者SQL_ID>'
AND SESSION_STATE = 'WAITING'
AND event = 'row lock wait'
ORDER BY sample_time DESC
LIMIT 20;
SELECT * FROM GV$OB_TRANSACTION_PARTICIPANTS WHERE tx_id = '<持有者TRANS_ID>';
SELECT * FROM GV$OB_TRANSACTION_SCHEDULERS WHERE tx_id = '<持有者TRANS_ID>';
-- 用 gv$ob_processlist.ID(应用层 session)追溯历史 SQL
SELECT usec_to_time(request_time) AS request_time_s,
t.svr_ip AS ip, t.svr_port AS port, t.tenant_id, t.trace_id, t.sid,
t.ret_code, t.elapsed_time / 1000000 AS elapsed_time_s, t.query_sql
FROM gv$ob_sql_audit t
WHERE sid IN (<应用层session_id_1>, <应用层session_id_2>)
ORDER BY request_time;
⚠️ gv$ob_sql_audit 记录在内存中,可能随时被淘汰,结果可能为空集。
见第 8 节。
orcl(Oracle 模式)tab1USERENV('SID')=284713):持有锁USERENV('SID')=297086):等待锁Step 1:从 GV$OB_LOCKS 拿持有者 TRANS_ID
SELECT * FROM GV$OB_LOCKS;
| ROLE | TRANS_ID | SESSION_ID | TYPE | LMODE | REQUEST | BLOCK | 含义 |
|---|---|---|---|---|---|---|---|
| 等待者 | 31008008 | 3221700502 | TR | NONE | X | 1 | SESSION 2 事务 31008008 等锁 |
| 阻塞者 | 31007974 | 3221692919 | TR | X | NONE | 0 | 事务 31007974 持锁 |
| - | 31007974 | 3221692919 | TX | X | NONE | 0 | 事务锁 |
| - | 31007974 | 3221692919 | TM | RX | NONE | 0 | 表锁 |
持有者 TRANS_ID = 31007974(来自等待者行 ID1)。
Step 2:从 sys 租户查 processlist(用 TRANS_ID 关联)
SELECT * FROM gv$ob_processlist WHERE TRANS_ID = 31007974;
| ID | USER | STATE | TRANS_ID | TRANS_STATE |
|---|---|---|---|---|
| 284713 | SYS | SLEEP | 31007974 | IMPLICIT_ACTIVE |
找到阻塞者应用层 session:ID=284713。
┌──────────────────────────────────┐
│ USERENV('SID') = 284713 │ ← Oracle 模式客户端视角
│ / connection_id() = 284713 │ ← MySQL 模式客户端视角
└──────────────┬───────────────────┘
│ (相同)
┌──────────────▼───────────────────┐
│ gv$ob_processlist.ID = 284713 │ ← OBServer 内部 session(可 KILL)
│ TRANS_ID = 31007974 │ ← 关联桥梁
└──────────────┬───────────────────┘
│ (TRANS_ID 关联)
┌──────────────▼───────────────────┐
│ GV$OB_TRANSACTION_SCHEDULERS │
│ .SESSION_ID = 3221692919 │ ← 事务调度器 session(不可 KILL)
│ GV$OB_TRANSACTION_PARTICIPANTS │
│ .SESSION_ID = 3221692919 │
│ GV$OB_LOCKS.ID2 = 3221692919 │
└──────────────┬───────────────────┘
│
┌──────────────▼───────────────────┐
│ gv$active_session_history │
│ .BLOCKING_SESSION_ID = │
│ 3221692919 │ ← 同样是事务调度器 session
└──────────────────────────────────┘
| 视角 | 过滤条件 | 阻塞者标识 |
|---|---|---|
GV$OB_LOCKS(持有者行) |
BLOCK=0 |
TRANS_ID=31007974, SESSION_ID=3221692919 |
gv$ob_processlist |
TRANS_ID=31007974 |
ID=284713 |
gv$ob_session |
ID=284713 |
TENANT=orcl, TRANS_ID=31007974 |
GV$OB_TRANSACTION_SCHEDULERS |
TX_ID=31007974 |
SESSION_ID=3221692919 |
GV$OB_TRANSACTION_PARTICIPANTS |
TX_ID=31007974 |
SESSION_ID=3221692919, ROLE=LEADER |
-- 一条 SQL 找出所有行锁阻塞的完整链路
SELECT
L.SVR_IP AS lock_svr_ip,
L.SVR_PORT AS lock_svr_port,
L.TENANT_ID,
L.ID1 AS holder_trans_id,
L.ID2 AS holder_scheduler_sid,
L.ID3 AS rowkey,
L.CTIME / 1000000 AS wait_seconds,
P.ID AS holder_ob_sid,
P.USER AS holder_user,
P.HOST AS holder_host,
P.PROXY_SESSID AS holder_proxy_sid,
P.INFO AS holder_sql,
P.STATE AS holder_state,
P.TIME AS holder_idle_seconds,
P.SVR_IP AS holder_svr_ip
FROM GV$OB_LOCKS L
LEFT JOIN gv$ob_processlist P
ON P.TRANS_ID = L.ID1
AND P.SVR_IP = L.SVR_IP
AND P.TENANT_ID = L.TENANT_ID
WHERE L.BLOCK = 1
AND L.CTIME > 10000000 -- 10 秒以上
ORDER BY L.CTIME DESC;
| 阻塞者状态 | MySQL 模式命令 | Oracle 模式命令 |
|---|---|---|
STATE=ACTIVE 且 COMMAND=Query(正在执行 SQL) |
KILL QUERY <id>; |
ALTER SYSTEM KILL QUERY '<id>,@<SVR_IP>:<SVR_PORT>' TENANT = <tenant>; |
STATE=ACTIVE 且 COMMAND=Sleep(空闲连接持锁) |
KILL <id>; |
ALTER SYSTEM KILL SESSION '<id>,@<SVR_IP>:<SVR_PORT>' TENANT = <tenant>; |
| 长事务彻底回滚 | KILL <id>; + 监控回滚 |
ALTER SYSTEM KILL SESSION ...; + 监控回滚 |
-- 终止当前正在执行的语句(保持连接)
KILL QUERY 3222040188;
-- 终止整个连接
KILL 3222040188;
-- 被 KILL 后的客户端表现
-- KILL QUERY:ERROR 1317 (70100): Query execution was interrupted
-- KILL:ERROR 2013 (HY000): Lost connection to MySQL server during query
-- 终止当前正在执行的语句(保持连接)
ALTER SYSTEM KILL QUERY '284713,@192.168.56.213:2882' TENANT = orcl;
-- 终止整个连接
ALTER SYSTEM KILL SESSION '284713,@192.168.56.213:2882' TENANT = orcl;
关键点:
- KILL 参数中的 id 是 gv$ob_processlist.ID(不是 GV$OB_LOCKS.SESSION_ID)
- 必须指定 SVR_IP(MySQL 模式可选,Oracle 模式必填)
- 多租户环境必须 TENANT = <tenant>
-- 阻塞者 ID=284713,SVR_IP=192.168.56.213,租户 orcl
-- MySQL 模式:
KILL 284713;
-- Oracle 模式:
ALTER SYSTEM KILL SESSION '284713,@192.168.56.213:2882' TENANT = orcl;
坑 1:SESSION_ID 直接 JOIN 错乱
-- 错误 ❌:GV$OB_LOCKS.SESSION_ID ≠ gv$ob_processlist.ID
SELECT * FROM GV$OB_LOCKS L
JOIN gv$ob_processlist P
ON L.SESSION_ID = P.ID;
坑 2:从 ASH 找阻塞者直接查 processlist.ID
-- 错误 ❌:Empty set
SELECT * FROM gv$ob_processlist
WHERE ID = (SELECT BLOCKING_SESSION_ID FROM gv$active_session_history ...);
-- BLOCKING_SESSION_ID 是事务调度器 session,不在 processlist 中
坑 3:KILL 错对象
坑 4:MySQL 模式误用 Oracle 模式 KILL 语法
-- 错误 ❌:MySQL 模式不应使用 ALTER SYSTEM KILL SESSION
ALTER SYSTEM KILL SESSION '284713,@192.168.56.213:2882' TENANT = orcl;
-- 正确:MySQL 模式
KILL 284713;
坑 5:误判长事务
gv$ob_processlist.TIME 大但 TRANS_STATE=SLEEP:可能只是空闲连接GV$OB_TRANSACTION_PARTICIPANTS.LAST_REQUEST_TIME 判定坑 6:CTIME 单位混淆
GV$OB_LOCKS.CTIME 单位是微秒,需 /1000000 转秒gv$ob_processlist.TIME 单位是秒-- 超过 60 秒的行锁等待
SELECT * FROM GV$OB_LOCKS WHERE BLOCK = 1 AND CTIME > 60000000;
┌─────────────────────────────────────────────────────────────┐
│ Step 1: GV$OB_LOCKS WHERE BLOCK=1 │
│ → 拿持有者 ID1(TRANS_ID)和等待者 SESSION_ID │
│ → 拿 ID3 解析 TABLET_ID-ROWKEY │
└────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 2 (可选): dba_ob_table_locations WHERE tablet_id=... │
│ → 反查表/分区位置(zone、svr_ip、role) │
└────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 3: gv$ob_processlist WHERE TRANS_ID = ID1 │
│ → 拿应用层 ID(可 KILL 的 session)和 SVR_IP │
└────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 4: 验证 ID=284713 的 INFO/SQL_ID/USER │
│ → 业务/重要操作 → 联系;空闲/慢 SQL → KILL │
└────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 5: KILL(按模式分) │
│ MySQL 模式: KILL QUERY <id> | KILL <id> │
│ Oracle 模式: ALTER SYSTEM KILL SESSION 'id,@ip:port'... │
└────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 6: 等待者自动唤醒,ASH/SQL_AUDIT 复盘根因 │
└─────────────────────────────────────────────────────────────┘
SELECT text FROM dba_views WHERE view_name = 'GV$OB_LOCKS';
返回的是 4 个 UNION ALL:
| # | 来源表 | TYPE | BLOCK | 含义 |
|---|---|---|---|---|
| 1 | ALL_VIRTUAL_LOCK_WAIT_STAT |
TR | 1 | 等待者(只有这一类 BLOCK=1) |
| 2 | ALL_VIRTUAL_TRANS_LOCK_STAT (ROWKEY NOT NULL) |
TR | 0 | 行锁持有者 |
| 3 | ALL_VIRTUAL_TRANS_LOCK_STAT (GROUP BY) |
TX | 0 | 事务锁持有者 |
| 4 | ALL_VIRTUAL_OBJ_LOCK JOIN 事务视图 |
TM/UL | 0 | 对象/表锁持有者 |
第 1 部分(等待者)关键字段:
SELECT SVR_IP, SVR_PORT, TENANT_ID, TRANS_ID, SESSION_ID,
'TR' AS TYPE,
HOLDER_TRANS_ID AS ID1, -- 持有者 trans_id
HOLDER_SESSION_ID AS ID2, -- 持有者事务调度器 session
CONCAT(TABLET_ID, '-', ROWKEY) AS ID3,
'NONE' AS LMODE,
LOCK_MODE AS REQUEST,
TIME_AFTER_RECV AS CTIME,
1 AS BLOCK
FROM SYS.ALL_VIRTUAL_LOCK_WAIT_STAT
重要细节:
SESSION_ID 是等待者自己;ID1/ID2 是持有者的 TRANS_ID/SESSION_IDSESSION_ID、ID1、ID2 都是自己的ID3 是 CONCAT(TABLET_ID, '-', ROWKEY)所以一个完整的"锁链"在 GV$OB_LOCKS 中表现为:
BLOCK=1 等待者行(ID1/ID2 指持有者)BLOCK=0 持有者行(同 TRANS_ID)在 OB V4 行锁模式中,GV$OB_LOCKS.SESSION_ID 是事务调度器 session,gv$ob_processlist.ID 是应用层 session,两者通过 TRANS_ID 桥接。要 KILL 阻塞会话,始终用 gv$ob_processlist 中通过 TRANS_ID 查到的 ID + SVR_IP。MySQL 模式用 KILL QUERY/KILL,Oracle 模式用 ALTER SYSTEM KILL SESSION。
这套排查思路在 V4.2.x 系列环境验证有效。如果你在 OceanBase 或分布式数据库运维中还遇到过其他疑难锁问题,也欢迎到 云栈社区 与更多 DBA 同行交流实战经验。