cat obdiag_lock_conflict_20240808175840/record
+------------------------------------------------------------------------------------------------------------------------------------------+
| record |
+------+-----------------------------------------------------------------------------------------------------------------------------------+
| step | info |
+------+-----------------------------------------------------------------------------------------------------------------------------------+
| 1 | by select * from oceanbase.GV$OB_LOCKS where BLOCK=1; the len is 2 |
| 2 | get holding_lock trans_id:31794294 |
| 3 | get holding_lock_session_id:3222014434 |
| 4 | wait_lock_trans_id is 31794336 |
| 5 | get wait_lock_session_id:3222015259 |
| 6 | exec sql: SELECT * FROM oceanbase.gv$OB_SQL_AUDIT where SID="3222014434"; to get |
| | holding_lock_sql_info. |
| 7 | holding_lock_session_id: 3222014434; not find sql_info on gv$OB_SQL_AUDIT |
+------+-----------------------------------------------------------------------------------------------------------------------------------+
The suggest: holding_lock_session_id: 3222014434; wait_lock_session_id : 3222015259, sql_info: not find.
Lock conflicts can be ended by killing holding_lock_session_id or wait_lock_session_id
其次,以典型的历史锁冲突场景为例。当发生锁冲突我们没来得及执行分析命令或当下不知道执行什么命令,希望找到历史锁冲突时,有两种方法:
👉 第一种方法:通过 OCP,会发现响应时间远远大于执行时间。
👉 第二种方式:通过 GV$OB_SQL_AUDIT: 命令,可以看到响应时间。
QUERY_SQL:select * from config where config_name = 'test rule' for update;
REQUEST_TIME:1723187738073566
ELAPSED_TIME:4513197
NET_TIME:0
NET_WAIT_TIME:0
QUEUE_TIME:0
DECODE_TIME:0
GET_PLAN_TIME:103964
EXECUTE_TIME:43923
RETRY_CNT:3403
如果响应时间大于执行时间,往往会有多次重复执行,那么结合重复次数就可以判断是否发生了锁冲突。
接下来我们使用 obdiag 收集日志:
obdiag gather log --from='2024-08-09 15:10:00' --to='2024-08-09 15:30:00' --grep="ret=-6004"
从日志中筛选和锁冲突相关的错误代码,比如 6003/6004/6005,从日志中找到代码。
[2024-08-0915:20:52.514054]WDIAG[STORAGE.TRANS]inner_lock_for_read(ob_tx_data_functor.cpp:290)[279073][T1006_TenantInf][T1005][YB420AFA0270-0006196BA9DFF148-0-0][lt=4][errcode=-6004]lock_for_readneedretry(ret=-6004,tx_data={tx_id:{txid:325843478},ref_cnt:1,state:"RUNNING",commit_version:{val:1723188052513540049,v:0},start_scn:{val:1723188052513540049,v:0},end_scn:{val:18446744073709551615,v:3}
这些代码意味着集群中出现过历史锁冲突,如此便可以快速定位历史问题。
最后一个例子是 obdiagSQL 优化。假设 test1 和 test2 是结构完全相同的两张表,但 test2 的数据量远大于 test1。连接条件为 t1.c1=t2.c1:
create table test1 (c1 int primary key, c2 int, key t1_i1(c2) local) partition by hash(c1) partitions15;
create table test2 (c1 int primary key, c2 int, key t2_i1(c2) local) partition by hash(c1) partitions15;
explain select * from test1 t1, test2 t2 where t1.c1 = t2.c1 and t1.c2 > 1 and t1.c2 < 1000;
======================================================================
|
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)|
| ----------------------------------------------------------------------
|0 |PX COORDINATOR | |89 |484 |
|1 |└─EXCHANGE OUT DISTR |:EX10001 |89 |416 |
|2 | └─HASH JOIN | |89 |264 |
|3 | ├─EXCHANGE IN DISTR | |89 |172 |
|4 | │ └─EXCHANGE OUT DISTR (PKEY) |:EX10000 |89 |138 |
|5 | │ └─PX PARTITION ITERATOR | |89 |62 |
|6 | │ └─TABLE RANGE SCAN |t1(t1_i1)|89 |62 |
|7 | └─PX PARTITION ITERATOR | |100 |63 |
|8 | └─TABLE FULL SCAN |t2 |100 |63 |
====================================================================== |
在执行计划中,会对 test2 做全表扫描,但这不是我们想要的,这时,我们就可以通过 obdiag 命令去收集执行计划。
obdiag gather plan_monitor --trace_id=TRACE_ID --env="{db_connect='-h127.0.0.1 -P2881 -utest@test -p****** -Dtest'}"
从收集结果中可以发现,test2 的估行和吐行差距过大,统计信息有问题。
call dbms_stats.gather_table_stats('test', 'test2');
通过 obdiag 重新统一信息,就可以解决问题。obdiag 简单易用,帮助我们解决了很多问题,运维效率不断提升。因此,我也希望投入更多精力,为 obdiag 做一些贡献。
(二)开发者视角的 obdiag
OceanBase 的敏捷诊断工具兴趣小组,即 obdiagSIG,是一个建设并推广 obdiag 工具及生态的开源小组,目标是打造一个集用户体验卓越、功能强大、社群活跃于一体的 OceanBase 诊断生态系统。
在参与共建的过程中,我发现了很多与用户运维视角不一样的东西。例如 obdiag rca 锁冲突场景,对于使用者而言,有时无法采集到导致锁冲突的会话 ID,而在开发者视角,发现导致这个问题出现的根本原因在于,obdiag 是通过 gv$ob_lock的trans_id 和 gv$ob_transaction_participants 关联获取 tx_id,然后关联 gv$ob_transaction_participants,但 gv$ob_transaction_participants 是集群视图会取到多条数据。而用户持有的会话只是连接到后端的一个 OBServer 上,其他的会话 ID 显示为 0,导致用户无法采集到相应结果。
优化逻辑比较简单,快速编辑本地运行目录中根因分析相关的脚本文件:obdiag/rca/lock_conflict_scene.py。只需要加一个条件:计算 ID 不等于零。然后把对应的数据查出来,这时用户想查询的会话 ID 就会显示。
优化后:
cat obdiag_lock_conflict_20240808175840/record
+------------------------------------------------------------------------------------------------------------------------------------------+
| record |
+------+-----------------------------------------------------------------------------------------------------------------------------------+
| step | info |
+------+-----------------------------------------------------------------------------------------------------------------------------------+
| 1 | by select * from oceanbase.GV$OB_LOCKS where BLOCK=1; the len is 2 |
| 2 | get holding_lock trans_id:31794294 |
| 3 | get holding_lock_session_id:0 |
| 4 | wait_lock_trans_id is 31794336 |
| 5 | get wait_lock_session_id:3222015259 |
| 6 | exec sql: SELECT * FROM oceanbase.gv$OB_SQL_AUDIT where SID="0"; to get |
| | holding_lock_sql_info. |
| 7 | holding_lock_session_id: 3222014434; not find sql_info on gv$OB_SQL_AUDIT |
+------+-----------------------------------------------------------------------------------------------------------------------------------+
The suggest: holding_lock_session_id: 0; wait_lock_session_id : 3222015259, sql_info: not find.
Lock conflicts can be ended by killing holding_lock_session_id or wait_lock_session_id
再比如 obdiag display 功能共建,此前我们在运维过程中需要用到大量的 SQL 及命令,这些 SQL 和命令管理起来非常麻烦,往往都会记录在自己的“小本本”上,需要用到的时候再去翻找对应命令,而经验不足的用户在方面可能会无从下手。
因此,我们借助 OceanBase 官方的能力,将常用的一些命令和 SQL 集成到 obdiag 中,只需要一条命令,快速响应并展示结果,降低运维的难度,让小白用户也能像老鸟一样在 OceanBase 的海洋中遨游。
#展示场景增加
vi~/.obdiag/display/task/observer/demo.yaml:
info_en:"[test]"
info_cn:"[测试case]"
task:
-version:"[3.1.0,3.2.4]"
steps:
{steps_object}
-version:[4.2.0.0,4.3.0.0]
steps:
{steps_object}
目前的一些成果展示如下:
想了解更多干货,可通过下方扫码关注
详情咨询
可扫码添加上智启元官方客服微信👇