---
title: 行锁问题排查介绍-OceanBase数据库使用指南
description: 了解OceanBase数据库在实际应用中关于 行锁问题排查介绍相关的常见问题和使用技巧，帮助您快速解决 行锁问题排查介绍的难题。
---
切换语言

- 简体中文
- English

划线反馈

# 行锁问题排查介绍

更新时间：2024-08-29 02:31

适用版本： V3.2.x、V3.1.x、V4.0.x、V4.1.x、V4.2.x 内容类型：Troubleshoot  

本文介绍行锁相关的问题以及排查的方法。 以行锁为单位，可以提出与行锁有密切关系的两个对象：行锁的持有者和行锁的等待者。如果能够将这两个信息进行监控，那么对排查行锁的相关问题，将带来很大的帮助。本质来讲，行锁冲突都是由事务引起的。上述两个对象，都是隶属于事务，因此对活跃事务的监控也是必不可少的。

## schema 详细介绍

### 活跃事务（__all_virtual_trans_stat）

```shell
OceanBase (root@oceanbase)> desc __all_virtual_trans_stat;
+---------------------------+---------------+------+-----+---------+-------+
| Field                     | Type          | Null | Key | Default | Extra |
+---------------------------+---------------+------+-----+---------+-------+
| tenant_id                 | bigint(20)    | NO   | PRI | NULL    |       |
| svr_ip                    | varchar(32)   | NO   | PRI | NULL    |       |
| svr_port                  | bigint(20)    | NO   | PRI | NULL    |       |
| inc_num                   | bigint(20)    | NO   | PRI | NULL    |       |
| session_id                | bigint(20)    | NO   |     | NULL    |       |
| proxy_id                  | varchar(512)  | NO   |     | NULL    |       |
| trans_type                | bigint(20)    | NO   |     | NULL    |       |
| trans_id                  | varchar(512)  | NO   |     | NULL    |       |
| is_exiting                | bigint(20)    | NO   |     | NULL    |       |
| is_readonly               | bigint(20)    | NO   |     | NULL    |       |
| is_decided                | bigint(20)    | NO   |     | NULL    |       |
| active_memstore_version   | varchar(64)   | NO   |     | NULL    |       |
| partition                 | varchar(64)   | NO   |     | NULL    |       |
| participants              | varchar(1024) | NO   |     | NULL    |       |
| autocommit                | bigint(20)    | NO   |     | NULL    |       |
| trans_consistency         | bigint(20)    | NO   |     | NULL    |       |
| ctx_create_time           | timestamp(6)  | YES  |     | NULL    |       |
| expired_time              | timestamp(6)  | YES  |     | NULL    |       |
| refer                     | bigint(20)    | NO   |     | NULL    |       |
| sql_no                    | bigint(20)    | NO   |     | NULL    |       |
| state                     | bigint(20)    | NO   |     | NULL    |       |
| part_trans_action         | bigint(20)    | NO   |     | NULL    |       |
| lock_for_read_retry_count | bigint(20)    | NO   |     | NULL    |       |
+---------------------------+---------------+------+-----+---------+-------+
23 rows in set (0.01 sec)

```

该表中展示了该集群中所有参与者上下文的当前状态，关键参数说明如下：

- `svr_ip`: 表示该上下文创建的 server 地址。
 - `session_id`: 表示该事务所对应 session 的唯一标识。
 - `proxy_id`: 表示客户端（proxy/java client）所对应的`ip:port`。
 - `trans_id`: 表示事务的唯一标识。
 - `is_exiting`: 当前的事务上下文是否正在退出。
 - `partition`: 当前的事务上下文在哪个分区上创建。
 - `participants`: 当前事务的参与者列表。
 - `ctx_create_time` 事务上下文创建的时间。
 - `refer`: 表示 context 当前的引用计数。
 - `sql_no`: 表示当前 context 上最后一次执行 SQL 的 `sql_no`。
 - `state`: 表示 context 当前的状态：INIT/PREPARE/COMMIT/ABORT/CLEAR，值域为 0/1/2/3/4。
 - `part_trans_action`: 表示当前 context 最后一次操作的动作：START_TASK／END_TASK／COMMIT。
 - `lock_for_read_retry_count`: 表示当前 context 在 table scan 过程中，是否遇到过锁冲突重试，值越大，表示冲突越严重。

### 行锁持有者（__all_virtual_trans_lock_stat）

```shell
OceanBase (root@oceanbase)> desc __all_virtual_trans_lock_stat;
+-----------------+--------------+------+-----+---------+-------+
| Field           | Type         | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------+-------+
| tenant_id       | bigint(20)   | NO   | PRI | NULL    |       |
| trans_id        | varchar(512) | NO   | PRI | NULL    |       |
| svr_ip          | varchar(32)  | NO   | PRI | NULL    |       |
| svr_port        | bigint(20)   | NO   | PRI | NULL    |       |
| partition       | varchar(64)  | NO   | PRI | NULL    |       |
| rowkey    | varchar(512) | NO   | PRI | NULL    |       |
| session_id      | bigint(20)   | NO   |     | NULL    |       |
| proxy_id        | varchar(512) | NO   |     | NULL    |       |
| ctx_create_time | timestamp(6) | YES  |     | NULL    |       |
| expired_time    | timestamp(6) | YES  |     | NULL    |       |
+-----------------+--------------+------+-----+---------+-------+
10 rows in set (0.00 sec)

```

该表记录以行为单位，记录了当前集群所有活跃事务持有行的相关信息，关键参数说明如下：

- `rowkey`：表示内部表示的行消息。
 - `session_id`、`proxy_id`、`trans_id`：等其他参数都是为了跟活跃事务对应起来，不再一一赘述。

### 行锁等待者（写锁）

```shell
OceanBase (root@oceanbase)> desc __all_virtual_lock_wait_stat;
+-----------------+---------------------+------+-----+---------+-------+
| Field           | Type                | Null | Key | Default | Extra |
+-----------------+---------------------+------+-----+---------+-------+
| svr_ip          | varchar(32)         | NO   | PRI | NULL    |       |
| svr_port        | bigint(20)          | NO   | PRI | NULL    |       |
| table_id        | bigint(20)          | NO   | PRI | NULL    |       |
| rowkey          | varchar(512)        | NO   | PRI | NULL    |       |
| addr            | bigint(20) unsigned | NO   | PRI | NULL    |       |
| need_wait       | tinyint(4)          | NO   |     | NULL    |       |
| recv_ts         | bigint(20)          | NO   |     | NULL    |       |
| lock_ts         | bigint(20)          | NO   |     | NULL    |       |
| abs_timeout     | bigint(20)          | NO   |     | NULL    |       |
| try_lock_times  | bigint(20)          | NO   |     | NULL    |       |
| time_after_recv | bigint(20)          | NO   |     | NULL    |       |
| session_id      | bigint(20)          | NO   |     | NULL    |       |
| block_session_id | bigint(20)          | NO   |     | NULL    |       |
| type             | bigint(20)          | NO   |     | NULL    |       |
| lock_mode        | bigint(20)          | NO   |     | NULL    |       |
+-----------------+---------------------+------+-----+---------+-------+
12 rows in set (0.00 sec)

```

该表统计了当前集群中，所有正在等待行锁的请求/语句的相关信息，关键参数说明如下：

- `session_id`、`proxy_id`、`trans_id`：等其他参数都是为了跟活跃事务对应起来，不再一一赘述。
 - `lock_ts`: 表示该请求开始等锁的时间点（us）。
 - `abs_timeout`: 该语句的绝对超时时间（us）。
 - `try_lock_times`: 表示该语句曾经尝试过加锁的次数，值越大，表示锁冲突越严重。
 - `block_session_id`: 表示第一个等在这个行上的事务的 session。

#### 注意

在 OceanBase 数据库 V4.2 及之后版本，该表中新增了 `holder_trans_id` 列，用于记录实际持锁的事务，用户可通过 `holder_trans_id` 结合 `__all_virtual_trans_stat` 表查找到实际的 `session_id`，并尝试通过 kill session 来杀死事务。需要注意的是 `holder_trans_id` 与 `block_session_id` 无实际的一一对应关系，`block_session_id` 的语义仍为上述语义。

等行锁其实分为两种情况，写写冲突，读写冲突，这里的读写之所以会冲突，是因为该语句的 `read_snapshot_version` > 该事务的 `prepare_version`，但 `commit version` 不确定。换言之，需要等该事务形成 `commit version` 之后，才能决定是否要读到该事务的修改。 上述虚拟表 `__all_virtual_lock_wait_stat`，展示写写冲突的情况。对于读写冲突的场景，目前我们做的比较简单，用`__all_virtual_trans_stat` 的后两个字段（`part_trans_action`，`lock_for_read_retry_count`）来表示，如果`part_trans_action＝START_TASK，lock_for_read_retry_count`，可以肯定当前的语句在等行锁，很可能是持有行锁的事务异常，导致一直没有提交。

#### 注意

在 OceanBase 数据库 V4.x 版本上 `__all_virtual_trans_stat` 中移除了 `lock_for_read_retry_count` 字段，排查因读写冲突造成的异常 hang 需要在日志中检索是否存在大量错误码为 `6004` 的日志。

具体请详细参见：[OceanBase 数据库 V4.2 版本，关于锁冲突问题的排查手册](https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000000664387?back=kb)

## 示例场景

### 场景 1: 想找到某个事务一直无法成功的原因

场景: 业务由于使用了一个较大的事务超时时间，且存在一个 session 中的未知长事务占有行锁，阻塞别的事务进行，该如何找到这个事务并将其杀死。

#### 方案 1: 通过加不上锁的事务来找到一直占有锁的事务

第一步: 根据加不上锁的事务的 session id，找到等锁的信息，我们就可以通过 rowkey 这一行知道他等在主键为 `oceanbase` 的行上。

```shell
obclient> select * from __all_virtual_lock_wait_stat where session_id = 3221580756\G
*************************** 1. row ***************************
          svr_ip: 11.166.xx.xx
        svr_port: 4xxxx
        table_id: 1101710651081554
          rowkey: table_id=1101710651081554 hash=779dd9b202397d7 rowkey_object=[{"VARCHAR":"oceanbase", collation:"utf8mb4_general_ci"}]
            addr: 140433355180784
       need_wait: 1
         recv_ts: 1600440077959302
         lock_ts: 1600440077960167
     abs_timeout: 1600450077859302
  try_lock_times: 1
 time_after_recv: 1307610861
      session_id: 3221580756
block_session_id: 3221580756
            type: 0
       lock_mode: 0
1 row in set (0.01 sec)

```

第二步: 之后我们通过主键 oceanbase，就可以找到对应的持有 oceanbase 行锁的事务 trans id 和其 session id 。

```shell
obclient> select * from __all_virtual_trans_lock_stat where rowkey like '%oceanbase%'\G
*************************** 1. row ***************************
      tenant_id: 1002
       trans_id: {hash:6605492148156030705, inc:3284929, addr:"11.166.80.69:48270", t:1600440036535233}
         svr_ip: 11.166.xx.xx
       svr_port: 4xxxx
      partition: {tid:1101710651081554, partition_id:0, part_cnt:0}
       table_id: 1101710651081554
         rowkey: table_id=1101710651081554 hash=779dd9b202397d7 rowkey_object=[{"VARCHAR":"oceanbase", collation:"utf8mb4_general_ci"}]
     session_id: 3221577520
       proxy_id: NULL
ctx_create_time: 2020-09-18 22:41:03.583285
   expired_time: 2020-09-19 01:27:16.534919
1 row in set (0.05 sec)

```

第三步: 杀掉对应 session 的事务:

```shell
obclient> kill 3221577520;
Query OK, 0 rows affected (0.00 sec)

```

#### 方案 2: 通过执行较长时间的事务来找到

第一步: 根据事务的时间，找到执行时间最长且未结束的事务 trans id。

```shell
obclient> select * from __all_virtual_trans_lock_stat order by ctx_create_time limit 5\G
*************************** 1. row ***************************
      tenant_id: 1002
       trans_id: {hash:6605492148156030705, inc:3284929, addr:"11.166.80.69:48270", t:1600440036535233}
         svr_ip: 11.166.xx.xx
       svr_port: 4xxxx
      partition: {tid:1101710651081554, partition_id:0, part_cnt:0}
       table_id: 1101710651081554
         rowkey: table_id=1101710651081554 hash=779dd9b202397d7 rowkey_object=[{"VARCHAR":"oceanbase", collation:"utf8mb4_general_ci"}]
     session_id: 3221577520
       proxy_id: NULL
ctx_create_time: 2020-09-18 22:41:03.583285
   expired_time: 2020-09-19 01:27:16.534919
1 row in set (0.05 sec)

```

第二步：通过事务 `trans_id` 找到其所持有的所有锁，明确是否是所需要杀掉的事务（这里所获取的锁消息可能不全）。执行如下命令就可以查询所持有的所有锁消息。

```shell
obclient> select * from __all_virtual_trans_lock_stat where trans_id like '%hash:6605492148156030705, inc:3284929%'\G
*************************** 1. row ***************************
      tenant_id: 1002
       trans_id: {hash:6605492148156030705, inc:3284929, addr:"11.166.xx.xx:4xxxx", t:1600440036535233}
         svr_ip: 11.166.xx.xx
       svr_port: 4xxxx
      partition: {tid:1101710651081554, partition_id:0, part_cnt:0}
       table_id: 1101710651081554
         rowkey: table_id=1101710651081554 hash=779dd9b202397d7 rowkey_object=[{"VARCHAR":"oceanbase", collation:"utf8mb4_general_ci"}]
     session_id: 3221577520
       proxy_id: NULL
ctx_create_time: 2020-09-18 22:41:03.583285
   expired_time: 2020-09-19 01:27:16.534919
*************************** 2. row ***************************
      tenant_id: 1002
       trans_id: {hash:6605492148156030705, inc:3284929, addr:"11.166.xx.xx:4xxxx", t:1600440036535233}
         svr_ip: 11.166.80.69
       svr_port: 48270
      partition: {tid:1101710651081554, partition_id:0, part_cnt:0}
       table_id: 1101710651081554
         rowkey: table_id=1101710651081554 hash=89413aecf767cd7 rowkey_object=[{"VARCHAR":"ob", collation:"utf8mb4_general_ci"}]
     session_id: 3221577520
       proxy_id: NULL
ctx_create_time: 2020-09-18 22:41:03.583285
   expired_time: 2020-09-19 01:27:16.534919
2 rows in set (0.05 sec)

```

第三步: 若明确是对应的事务，杀掉对应 session 的事务。

```

### 场景 2: 提前知道某一行加锁失效的场景

场景：为什么某一行（给定 rowkey 部分字段）总超时，是不是有锁冲突？ 假设 rowkey 包含字符串 "zhangfei"，排查流程如下。 第一步：根据 rowkey 查询 `__all_virtual_trans_lock_stat` 找到对应的持锁事务。

```shell
OceanBase (root@oceanbase)> select * from __all_virtual_trans_lock_stat where memtable_key like '%zhangfei%'\G;
*************************** 1. row ***************************
      tenant_id: 1
       trans_id: {hash:6124095709354809361, inc:249476, addr:"10.101.xxx.xx:5xxxx", t:1529075759177984}
         svr_ip: 10.101.xxx.xx
       svr_port: 5xxxx
      partition: {tid:1099511677778, partition_id:0, part_cnt:0}
   memtable_key: {table_id:1099511677778, hash_val:7893135555906369137, buf:"table_id=1099511677778 hash=3fb183d083d6d9f1 rowkey_object=[{"VARCHAR":"zhangfei", collation:"utf8mb4_general_ci"}] "}
     session_id: 2147549190
       proxy_id: NULL
ctx_create_time: 2018-06-15 23:16:35.695821
   expired_time: 2018-06-15 23:32:39.177533
1 row in set (0.03 sec)

```

第二步：根据上述结果，查询活跃事务虚拟表 `__all_virtual_trans_stat` 找到对应的事务和 session 。

```shell
OceanBase (root@oceanbase)> select * from __all_virtual_trans_stat where trans_id like '%6124095709354809361%'\G;
*************************** 1. row ***************************
                tenant_id: 1
                   svr_ip: 10.101.xxx.xx
                 svr_port: 5xxxx
                  inc_num: 249476
               session_id: 2147549190
                 proxy_id: NULL
               trans_type: 0
                 trans_id: {hash:6124095709354809361, inc:249476, addr:"10.xxx.xxx.xx:59804", t:1529075759177984}
               is_exiting: 0
              is_readonly: 0
               is_decided: 0
  active_memstore_version: 0-0-0
                partition: {tid:1099511677778, partition_id:0, part_cnt:0}
             participants: [{tid:1099511677778, partition_id:0, part_cnt:0}]
               autocommit: 0
        trans_consistency: 0
          ctx_create_time: 2018-06-15 23:16:35.695821
             expired_time: 2018-06-15 23:32:39.177533
                    refer: 1073741826
                   sql_no: 1
                    state: 0
        part_trans_action: 2
lock_for_read_retry_count: 1
1 row in set (0.01 sec)

```

可以看到该事务是单分区事务（trans_type＝0），在 `2018-06-15 23:16:35.695821` 时刻完成事务上下文的创建。该 context 最后一步的动作为 END_TASK，一直没有收到 commit 命令，因此怀疑，客户端尚未 commit，导致事务没有结束，从而引起行锁冲突。 当然，也有另外一种可能，其实客户端异常宕机，导致 OBServer 内部残留了部分连接一直没有断开，从而导致事务悬空，此所谓“悬挂事务”，这里可以通过下面的语句，进一步排查 session 的状态。

```shell
OceanBase (root@oceanbase)> select * from __all_virtual_processlist where id=2147549190\G;
*************************** 1. row ***************************
          id: 2147549190
        user: root
      tenant: sys
        host: 10.101.xxx.xx:60666
          db: oceanbase
     command: Sleep
        time: 466
       state: SLEEP
        info: NULL
      svr_ip: 10.101.xxx.xx
    svr_port: 59xxx
    sql_port: 59805
proxy_sessid: NULL
1 row in set (0.01 sec)

```

由此可知，上述 session 是创建者，即客户端 server ip 为 `10.101.xxx.xx`，此时可以去该 server 上排查客户端日志，进一步定位。

Previous

[锁冲突问题排查](https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000001012954)

Next

[事务超时问题排查指南](https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000000927385) ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
