OceanBase 数据库中的行锁问题有什么排查方式?

本文介绍行锁问题的排查思路,并提供部分排查案例。

适用版本

OceanBase 数据库所有版本

OceanBase 数据库所有版本

锁冲突问题的排查思路

在业务环境中,可以将锁抽象为两个与行锁有密切关系的对象,即行锁的持有者与等待者。而行锁的持有者与等待者都是事务的一部分,因此在监控活跃事务中监控锁的持有者与等待者会对锁冲突的问题排查提供很大的帮助。

OceanBase 数据库提供了以下虚拟表,分别用于监控活跃事务、行锁持有者与行锁等待者。

  • __all_virtual_trans_stat

    :用于监控活跃事务。

  • 各主要表列的说明如下表所示。

  • 列名

  • 说明

  • svr_ip

  • 表示创建该事务上下文的 OBServer 的 IP 地址。

  • session_id

  • 表示该事务上下文所属会话的唯一 ID。

  • proxy_id

  • 表示客户端 OBProxy/Java Client 对应的 IP 地址与端口号。

  • trans_id

  • 表示该事务的唯一 ID。

  • is_existing

  • 表示当前事务上下文是否正在退出。

  • partition

  • 表示当前事务上下文在哪个分区上创建。

  • participants

  • 当前事务的参与者列表。

  • ctx_create_time

  • 事务上下文的创建时间。

  • ref

  • 表示事务上下文当前的引用计数。

  • sql_no

  • 表示当前事务上下文上最后一次执行的 SQL 的

    sql_no

  • state

  • 表示事务上下文当前的状态。

  • 可能的状态有

    INIT

    PREPARE

    COMMIT

    ABORT

    CLEAR

  • part_trans_action

  • 表示该事务上下文上最后执行的动作。

  • 可能的状态有

    START_TASK

    END_TASK

    COMMIT

  • lock_for_read_entry_count

  • 表示该事务在表扫描过程中遇到锁冲突重试的次数。

  • __all_virtual_trans_lock_stat

    :记录了当前集群中所有活跃事务持有行锁的相关信息。

  • 各主要表列的说明如下表所示。

  • 列名

  • 说明

  • rowkey

  • 表示持有锁的行的 rowkey。

  • session_id

  • 持有锁的事务所属的会话唯一 ID。

  • proxy_id

  • 表示持有锁的事务所属客户端 OBProxy/Java Client 对应的 IP 地址与端口号。

  • trans_id

  • 表示持有锁的事务的唯一 ID。

  • __all_virtual_lock_wait_stat

    :统计了当前集群中所有等待行锁的请求或语句的相关信息。

  • 各主要表列的说明如下表所示。

  • 列名

  • 说明

  • session_id

  • 等待锁的事务所属的会话唯一 ID。

  • proxy_id

  • 表示等待锁的事务所属客户端 OBProxy/Java Client 对应的 IP 地址与端口号。

  • trans_id

  • 表示等待锁的事务的唯一 ID。

  • lock_ts

  • 表示该请求开始等待锁的时间点。

  • abs_timeout

  • 表示等待锁的语句的绝对超时时间。

  • try_lock_times

  • 表示等待锁的语句重试加锁的次数。

  • block_session_id

  • 表示在该行第一个等待事务的

    session_id

说明等待锁分为两种情况,即写写冲突与读写冲突。对于读写冲突的情况,是由于事务中读语句的

read_snapshot_version

大于写语句的

prepare_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

的值非零,则可以判断是发生了读写冲突。

字数限制,下接评论区——————

锁冲突问题的排查案例

场景一

业务使用了较大的超时事件,且存在一个会话中的未知长事务持有锁,阻塞了其他事务的执行,需要找到并停止该长事务。

  • 方案一:通过无法加锁的事务查询持有锁的事务。
  1. 通过无法加锁的事务的 
    session_id
    ,找到等待锁的行的 
    rowkey
  2. 可以发现等待锁的事务在等待主键为 
    pk
     的行。
obclient> select * from __all_virtual_lock_wait_stat where session_id = 3221580756\G
*************************** 1. row ***************************
          svr_ip: xxx.xxx.xx.xxx
       svr_port: xxxx
        table_id: 1101710651081554
          rowkey: table_id=1101710651081554 hash=779dd9b202397d7 rowkey_object=[{"VARCHAR":"pk", 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)


  1. 通过主键找到对应持有该行行锁的事务的 
    session_id
  2. 发现持有行锁的事务的 
    session_id
     为 
    3221577520
obclient> select * from __all_virtual_trans_lock_stat where rowkey like '%pk%'\G
*************************** 1. row ***************************
      tenant_id: 1002
       trans_id: {hash:6605492148156030705, inc:3284929, addr:"xxx.xxx.xx.xxx:xxxx", t:1600440036535233}
         svr_ip: xxx.xxx.xx.xxx
       svr_port: xxxx
      partition: {tid:1101710651081554, partition_id:0, part_cnt:0}
       table_id: 1101710651081554
         rowkey: table_id=1101710651081554 hash=779dd9b202397d7 rowkey_object=[{"VARCHAR":"pk", 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)
  1. 根据查到的 
    session_id
     停止该事务的会话。
obclient> KILL 3221577520


  • 方案二:通过查询长事务。
  1. 根据事务执行时间,找到执行时间最长且未结束事务的 
    trans_id
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:"xxx.xxx.xx.xxx:xxxx", t:1600440036535233}
         svr_ip: xxx.xxx.xx.xxx
       svr_port: xxxx
      partition: {tid:1101710651081554, partition_id:0, part_cnt:0}
       table_id: 1101710651081554
         rowkey: table_id=1101710651081554 hash=779dd9b202397d7 rowkey_object=[{"VARCHAR":"pk", 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)
  1. 通过事务的 
    trans_id
     找到其所持有的所有锁,并根据 
    rowkey
     明确哪一个是需要停止的服务。
  2. 下例中,第一行的 
    rowkey
     与上面查询的 
    rowkey
     相同,因此该事务即是持有锁的事务。
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:"xxx.xxx.xx.xxx:xxxx", t:1600440036535233}
         svr_ip: xxx.xxx.xx.xxx
       svr_port: xxxx
      partition: {tid:1101710651081554, partition_id:0, part_cnt:0}
       table_id: 1101710651081554
         rowkey: table_id=1101710651081554 hash=779dd9b202397d7 rowkey_object=[{"VARCHAR":"pk", 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:"xxx.xxx.xx.xxx:xxxx", t:1600440036535233}
         svr_ip: xxx.xxx.xx.xxx
       svr_port: xxxx
      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)
  1. 确认要停止的事务的 
    session_id
     后,停止对应事务的会话。
obclient> KILL 3221577520;
Query OK, 0 rows affected (0.00 sec)


场景二

业务反馈某一行上执行的事务总超时,已知该行的 

rowkey
,本场景以 
rowkey
 包含字符串 
test
 为例。

  1. 根据 
    rowkey
     查询 
    __all_virtual_trans_lock_stat
     表找到对应的持锁事务。
obclient> select * from __all_virtual_trans_lock_stat where memtable_key like '%test%'\G;
*************************** 1. row ***************************
      tenant_id: 1
       trans_id: {hash:6124095709354809361, inc:249476, addr:"xxx.xxx.xx.xxx:xxxx", t:1529075759177984}
         svr_ip: xxx.xxx.xx.xxx
       svr_port: xxxx
      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":"test", 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)
  1. 根据上述结果,查询活跃事务虚拟表 
    __all_virtual_trans_stat
     找到对应的事务的 
    session_id
  2. 发现持有行锁的事务的 
    session_id
     为 
    2147549190
OceanBase (root@oceanbase)> select * from __all_virtual_trans_stat where trans_id like '%6124095709354809361%'\G;
*************************** 1. row ***************************
                tenant_id: 1
                   svr_ip: xxx.xxx.xx.xxx
                 svr_port: xxxx
                  inc_num: 249476
               session_id: 2147549190
                 proxy_id: NULL
               trans_type: 0
                 trans_id: {hash:6124095709354809361, inc:249476, addr:"xxx.xxx.xx.xxx:xxxx", 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)
  1. 确认要停止的事务的 
    session_id
     后,停止对应事务的会话。
obclient> KILL 2147549190;
Query OK, 0 rows affected (0.00 sec)