本文介绍行锁问题的排查思路,并提供部分排查案例。
适用版本
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
的值非零,则可以判断是发生了读写冲突。
字数限制,下接评论区——————
锁冲突问题的排查案例
场景一
业务使用了较大的超时事件,且存在一个会话中的未知长事务持有锁,阻塞了其他事务的执行,需要找到并停止该长事务。
- 通过无法加锁的事务的
session_id
,找到等待锁的行的 rowkey
。 - 可以发现等待锁的事务在等待主键为
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)
- 通过主键找到对应持有该行行锁的事务的
session_id
。 - 发现持有行锁的事务的
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)
- 根据查到的
session_id
停止该事务的会话。
obclient> KILL 3221577520
- 根据事务执行时间,找到执行时间最长且未结束事务的
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)
- 通过事务的
trans_id
找到其所持有的所有锁,并根据 rowkey
明确哪一个是需要停止的服务。 - 下例中,第一行的
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)
- 确认要停止的事务的
session_id
后,停止对应事务的会话。
obclient> KILL 3221577520;
Query OK, 0 rows affected (0.00 sec)
场景二
业务反馈某一行上执行的事务总超时,已知该行的
rowkey
,本场景以
rowkey
包含字符串
test
为例。
- 根据
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)
- 根据上述结果,查询活跃事务虚拟表
__all_virtual_trans_stat
找到对应的事务的 session_id
。 - 发现持有行锁的事务的
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)
- 确认要停止的事务的
session_id
后,停止对应事务的会话。
obclient> KILL 2147549190;
Query OK, 0 rows affected (0.00 sec)