首批通过分布式安全可靠测评,为关键业务系统打造
OceanBase 数据库 V4.2 版本,关于锁冲突问题的排查手册
更新时间:2026-07-01 08:46
本文以 MySQL 模式为例,介绍在 OceanBase 数据库 V4.2.x 版本中有关锁冲突问题的排查。另外在 Oracle 模式下,锁冲突问题排查和锁冲突问题处理使用的 SQL 是一样的。
MySQL 模式下,锁冲突排查和锁冲突处理示例
登录业务租户的
test数据库。[admin@xxx]# mysql -h${DB_HOST} -P2883 -uroot@obmysql#obcluster -Dtest -A -p${DB_PASSWORD}在业务租户
test数据库中创建测试表。obclient [test]> CREATE TABLE tb_test_lock (c1 INT, c2 VARCHAR(20)); Query OK, 0 rows affected (0.087 sec)向测试表插入数据。
obclient [test]> INSERT INTO tb_test_lock values(1,'row 1'),(2,'row 2'),(3,'row 3'),(4,'row 4'),(5,'row 5'); Query OK, 5 rows affected (0.042 sec) Records: 5 Duplicates: 0 Warnings: 0提交插入数据。
obclient [test]> COMMIT; Query OK, 0 rows affected (0.035 sec)查询测试表。
obclient [test]> SELECT now(),* FROM tb_test_lock;输出结果如下:
+---------------------+------+-------+ | now() | c1 | c2 | +---------------------+------+-------+ | 2024-03-14 16:14:17 | 1 | row 1 | | 2024-03-14 16:14:17 | 2 | row 2 | | 2024-03-14 16:14:17 | 3 | row 3 | | 2024-03-14 16:14:17 | 4 | row 4 | | 2024-03-14 16:14:17 | 5 | row 5 | +---------------------+------+-------+ 5 rows in set (0.036 sec)打开新的 obclient 窗口会话 Session 1。
a. 设置 SQL 最大执行时间,单位是微秒。
obclient [test]> SET ob_query_timeout = 3600000000; -- 换算成秒等于 3600s。 Query OK, 0 rows affected (0.035 sec)b. 设置事务超时时间,单位为微秒。
obclient [test]> SET ob_trx_timeout = 3600000000; -- 换算成秒等于 3600s。 Query OK, 0 rows affected (0.035 sec)c. 设置事务空闲超时时间,即事务中两条语句之间的执行间隔超过该值时超时,单位为微秒。
obclient [test]> SET ob_trx_idle_timeout = 3600000000; -- 3600s Query OK, 0 rows affected (0.035 sec)d. 返回当前日期时间。
obclient [test]> SELECT now();输出结果如下:
+---------------------+ | now() | +---------------------+ 2024-03-14 16:14:32 | +---------------------+ 1 row in set (0.035 sec)e. 返回当前会话的 SESSION_ID,该 ID 是会话在客户端中的唯一标识。
obclient [test]> SELECT connection_id();输出结果如下:
+-----------------+ | connection_id() | +-----------------+ | 3222040188 | +-----------------+ 1 row in set (0.034 sec)f. 使用
BEGIN显式开启一个事务,通过SELECT ... FOR UPDATE的方式为读事务的对象加锁,从而达到阻塞写事务的目的。obclient [test]> BEGIN; Query OK, 0 rows affected (0.035 sec)obclient [test]> SELECT * FROM tb_test_lock WHERE c1 = 1 FOR UPDATE;输出结果如下:
+------+-------+ | c1 | c2 | +------+-------+ | 1 | row 1 | +------+-------+ 1 row in set (0.036 sec)打开新的 obclient 窗口会话 Session 2。
a. 设置 SQL 最大执行时间,单位是微秒。
obclient [test]> SET ob_query_timeout = 3600000000; -- 换算成秒等于 3600s。 Query OK, 0 rows affected (0.038 sec)b. 设置事务超时时间,单位为微秒。
obclient [test]> SET ob_trx_timeout = 3600000000; -- 换算成秒等于 3600s。 Query OK, 0 rows affected (0.038 sec)c. 设置事务空闲超时时间,即事务中两条语句之间的执行间隔超过该值时超时,单位为微秒。
obclient [test]> SET ob_trx_idle_timeout = 3600000000; -- 3600s Query OK, 0 rows affected (0.038 sec)d. 返回当前日期时间。
obclient [test]> SELECT now();输出结果如下:
+---------------------+ | now() | +---------------------+ | 2024-03-14 16:14:50 | +---------------------+ 1 row in set (0.038 sec)e. 返回当前会话的 SESSION_ID,该 ID 是会话在客户端中的唯一标识。
obclient [test]> SELECT connection_id();输出结果如下:
+-----------------+ | connection_id() | +-----------------+ | 3222040115 | +-----------------+ 1 row in set (0.038 sec)f. 使用
BEGIN显式开启一个事务,之后执行二次 DELETE 语句。obclient [test]> BEGIN; Query OK, 0 rows affected (0.034 sec)obclient [test]> DELETE FROM tb_test_lock WHERE c1 = 4; -- 语句 1。 Query OK, 1 row affected (0.036 sec)obclient [test]> DELETE FROM tb_test_lock WHERE c1 = 1; -- 语句 2,执行之后无返回信息。
锁冲突问题排查
锁冲突排查步骤示例。
登录业务租户的
oceanbase数据库查询。[admin@xxx]# mysql -h${DB_HOST} -P2883 -uroot@obmysql#obcluster -Doceanbase -A -p${DB_PASSWORD}切换当前的数据库。
obclient [test]> use oceanbase; Database changed当前用户各表锁情况以及表或者分区所处的位置信息。
a. 通过
gv$ob_locks视图获取持锁或请求锁的事务 ID 以及事务被阻塞信息。MySQL [oceanbase]> SELECT t.tenant_id,t.trans_id,t.type,t.id1,t.id2,t.lmode,t.request,round(t.ctime / 1000000) ctime_s,t.block FROM gv$ob_locks t WHERE t.block = 1 ORDER BY t.trans_id;输出结果如下:
+-----------+----------+------+----------+--------------------------------+-------+---------+---------+-------+ | tenant_id | trans_id | type | id1 | id2 | lmode | request | ctime_s | block | +-----------+----------+------+----------+--------------------------------+-------+---------+---------+-------+ | 1002 | 23723243 | TR | 200051 | 23723237-{"BIGINT UNSIGNED":1} | NONE | X | 20 | 1 | | 1002 | 23723243 | TX | 23723237 | NULL | NONE | X | 20 | 1 | +-----------+----------+------+----------+--------------------------------+-------+---------+---------+-------+ 2 rows in set (0.014 sec)b. 通过持有锁的事务
tablet_id的值,查询dba_ob_table_locations表,获取表或分区所在的 Zone 和 IP 地址和端口号,副本角色,数据库名等信息。MySQL [oceanbase]> SELECT zone,svr_ip,svr_port,role,tablet_id,database_name,table_name FROM dba_ob_table_locations WHERE tablet_id = 200051 ORDER BY zone,svr_ip,svr_port;输出结果如下:
+-------+---------------+----------+----------+-----------+---------------+--------------+ | zone | svr_ip | svr_port | role | tablet_id | database_name | table_name | +-------+---------------+----------+----------+-----------+---------------+--------------+ | zone1 | xx.xx.xx.xx | 2882 | FOLLOWER | 200051 | test | tb_test_lock | | zone2 | xx.xx.xx.xx | 2882 | FOLLOWER | 200051 | test | tb_test_lock | | zone3 | xx.xx.xx.xx | 2882 | LEADER | 200051 | test | tb_test_lock | +-------+---------------+----------+----------+-----------+---------------+--------------+ 3 rows in set (0.075 sec)综上通过
gv$ob_locks视图(用于显示当前用户各表持锁或请求锁的情况。)以及表dba_ob_table_locations(展示表或者分区所在的位置,包括:系统表、用户表、索引表等。),获取关于锁冲突方面的信息便于排查锁冲突问题。视图(
gv$ob_locks)与表(dba_ob_table_locations)中涉及锁冲突相关的字段含义如下:gv$ob_locks.trans_id为被 block 的trans_id。gv$ob_locks.type为TX的记录中,gv$ob_locks.id1为当前持有锁的trans_id。gv$ob_locks.type为TR的记录中,gv$ob_locks.id1为当前被锁的对象的dba_ob_table_locations.tablet_id,gv$ob_locks.id2为被锁的行。
可知以上示例排查步骤 4 中,
trans_id为23723243是被 block 的事务,trans_id为23723237是持有锁的事务。通过字段
tx_id的值(即步骤 3 中获取的trans_id值。),查询视图gv$ob_transaction_participants获取session_id值。MySQL [oceanbase]> SELECT round(now() - t.ctx_create_time) running_time_s,t.tenant_id,t.svr_ip,t.svr_port,t.session_id,t.tx_type,t.tx_id,t.state,t.action,t.ctx_create_time,t.last_request_time FROM gv$ob_transaction_participants t WHERE t.tx_id IN (23723243, 23723237);输出结果如下:
+----------------+-----------+---------------+----------+------------+-----------+----------+--------+--------+----------------------------+----------------------------+ | running_time_s | tenant_id | svr_ip | svr_port | session_id | tx_type | tx_id | state | action | ctx_create_time | last_request_time | +----------------+-----------+---------------+----------+------------+-----------+----------+--------+--------+----------------------------+----------------------------+ | 83 | 1002 | xx.xx.xx.xx | 2882 | 3222040115 | UNDECIDED | 23723243 | ACTIVE | START | 2024-03-14 16:14:28.764186 | 2024-03-14 16:15:10.533369 | | 94 | 1002 | xx.xx.xx.xx | 2882 | 3222040188 | UNDECIDED | 23723237 | ACTIVE | START | 2024-03-14 16:14:17.961048 | 2024-03-14 16:14:17.961048 | +----------------+-----------+---------------+----------+------------+-----------+----------+--------+--------+----------------------------+----------------------------+ 2 rows in set (0.019 sec)通过字段
id(会话 ID 的值,即步骤 4 中获取的session_id值。),查询视图gv$ob_processlist获取会话信息。MySQL [oceanbase]> SELECT t.svr_ip,t.svr_port,t.id,t.host,t.db,t.tenant,t.command,t.time,t.state,t.info,t.user_client_ip,t.retry_cnt,t.retry_info,t.trans_id,t.trans_state FROM gv$ob_processlist t WHERE t.id IN (3222040115, 3222040188);输出结果如下:
+---------------+----------+------------+------+---------------------+------+---------+---------+------+-------+------+----------------+-----------+------------+----------+-------------+ | svr_ip | svr_port | id | user | host | db | tenant | command | time | state | info | user_client_ip | retry_cnt | retry_info | trans_id | trans_state | +---------------+----------+------------+------+---------------------+------+---------+---------+------+-------+------+----------------+-----------+------------+----------+-------------+ | xx.xx.xx.xx | 2882 | 3222040188 | root | xx.xx.xx.xx:xxxxx | test | obmysql | Sleep | 84 | SLEEP | NULL | xx.xx.xx.xx | 0 | 0 | 23723237 | ACTIVE | | xx.xx.xx.xx | 2882 | 3222040115 | root | xx.xx.xx.xx:xxxxx | test | obmysql | Sleep | 2 | SLEEP | NULL | xx.xx.xx.xx | 24 | -6005 | 23723243 | ACTIVE | +---------------+----------+------------+------+---------------------+------+---------+---------+------+-------+------+----------------+-----------+------------+----------+-------------+ 2 rows in set (0.007 sec)可知以上示例排查步骤 5 中,
session_id为3222040115是被 block 的 session,session_id为3222040188是持有锁的 session。通过字段
sid的值(即步骤 5 中获取的session_id值。),查询视图gv$ob_sql_audit分析执行过的历史 SQL。该步骤的表的记录,可能会随时刷掉,因此可能查询为空集。MySQL [oceanbase]> SELECT usec_to_time(request_time) request_time_s,t.svr_ip ip,t.svr_port port,t.tenant_id,t.trace_id,t.sid,t.ret_code,t.elapsed_time / 1000000 elapsed_time_s,t.query_sql FROM gv$ob_sql_audit t WHERE sid IN (3222040115, 3222040188) ORDER BY request_time;输出结果如下:
+----------------------------+---------------+------+-----------+-----------------------------------+------------+----------+-----------------+----------------------------------------------------+ | request_time_s | ip | port | tenant_id | trace_id | sid | ret_code | elapsed_time(s) | query_sql | +----------------------------+---------------+------+-----------+-----------------------------------+------------+----------+-----------------+----------------------------------------------------+ | 2024-03-14 16:14:17.957421 | xx.xx.xx.xx | 2882 | 1002 | xxxxxxxxxxxx-xxxxxxxxxxxxxxxx-0-0 | 3222040188 | 0 | 0.0003 | SET @_min_cluster_version = '4.2.1.3'; | | 2024-03-14 16:14:17.958552 | xx.xx.xx.xx | 2882 | 1002 | xxxxxxxxxxxx-xxxxxxxxxxxxxxxx-0-0 | 3222040188 | 0 | 0.0002 | BEGIN | | 2024-03-14 16:14:17.959404 | xx.xx.xx.xx | 2882 | 1002 | xxxxxxxxxxxx-xxxxxxxxxxxxxxxx-0-0 | 3222040188 | 0 | 0.0023 | SELECT * FROM tb_test_lock WHERE c1 = 1 FOR UPDATE | | 2024-03-14 16:14:28.761535 | xx.xx.xx.xx | 2882 | 1002 | xxxxxxxxxxxx-xxxxxxxxxxxxxxxx-0-0 | 3222040115 | 0 | 0.0003 | SET @_min_cluster_version = '4.2.1.3'; | | 2024-03-14 16:14:28.762309 | xx.xx.xx.xx | 2882 | 1002 | xxxxxxxxxxxx-xxxxxxxxxxxxxxxx-0-0 | 3222040115 | 0 | 0.0001 | BEGIN | | 2024-03-14 16:14:28.762747 | xx.xx.xx.xx | 2882 | 1002 | xxxxxxxxxxxx-xxxxxxxxxxxxxxxx-0-0 | 3222040115 | 0 | 0.0021 | DELETE FROM tb_test_lock WHERE c1 = 4 | +----------------------------+---------------+------+-----------+-----------------------------------+------------+----------+-----------------+----------------------------------------------------+ 6 rows in set (0.199 sec)
锁冲突问题处理
结合业务,根据 锁冲突问题排查 一节,分析造成锁的原因。必要情况下可从应用层面停止持有锁的相关操作等。
生产环境紧急情况下,与 OceanBase 技术支持确认后,可考虑执行如下 SQL。
如果持有锁的 session (
gv$ob_processlist.id= 3222040188)中,gv$ob_processlist.state为ACTIVE且gv$ob_processlist.command不为Sleep,可以考虑执行KILL QUERY 3222040188终止连接当前正在执行的语句。id: 3222040188 command: Query state: ACTIVE info: SELECT sleep(30)a. 终止连接当前正在执行的语句 (但是会保持连接)。
MySQL [oceanbase]> KILL QUERY 3222040188;b. 被
KILL QUERY的 session 报错如下。MySQL [test]> SELECT sleep(20); ERROR 1317 (70100): Query execution was interrupted如果持有锁的 session (
gv$ob_processlist.id= 3222040188)中,gv$ob_processlist.state为ACTIVE且gv$ob_processlist.command为Sleep,可以考虑执行KILL 3222040188终止连接。id: 3222040188 command: Sleep state: SLEEP info: NULLa. 终止连接。
MySQL [oceanbase]> KILL 3222040188;b. 被
KILL的 session 报错如下。MySQL [test]> SELECT now(); ERROR 2013 (HY000): Lost connection to MySQL server during queryMySQL [test]> SELECT now(); ERROR 2006 (HY000): MySQL server has gone away No connection. Trying to reconnect... Connection id: 3221670379 Current database: test输出结果如下:
+---------------------+ | now() | +---------------------+ | 2024-03-14 16:21:47 | +---------------------+ 1 row in set (0.005 sec)注意
锁冲突问题处理中
3222040188为持有锁的session_id。
适用版本
OceanBase 数据库 V4.2.x 版本。