OceanBase:行锁分析方法

说明

当发生行锁阻塞,我们可能要做两件事情:

  • 应急解开锁阻塞
  • 如果锁阻塞频繁发生,导致业务响应时间变长,需要帮助业务找到相关逻辑,优化业务,减少锁阻塞的发生

本文档主要介绍V3版本中找出阻塞源、分析业务 SQL 的方法。

1. 制造锁阻塞场景

--session1
--为方便观察,把超时参数调大
set global ob_query_timeout = 3600000000;
set global ob_trx_timeout = 3600000000;
set global ob_trx_idle_timeout = 3600000000;
ob_trx_lock_timeout 默认为-1,锁等待没有超时时间

--准备数据
create table t1 (id number,name varchar2(12),primary key(id));
insert into t1 values(1,'A1');
insert into t1 values(2,'B1');
commit;
select * from t1;

--开启事务加锁
start transaction;
select * from t1 where id = 1 for update;

--session2
--开启事务加锁
start transaction;
select * from t1 where id = 2 for update;
select * from t1 where id = 1 for update;

--session1 制造死锁场景(默认不开启死锁检测)
select * from t1 where id = 2 for update;

2. 通过视图分析锁阻塞

如果有现场,我们可以通过系统视图分析锁阻塞。

2.1 查找锁的阻塞关系

先给出一个直接查出锁阻塞关系的方法,然后再介绍 __all_virtual_lock_wait_stat、__all_virtual_trans_lock_stat 两个视图的限制。

SELECT 
wait.TENANT_ID,
wait.SVR_IP,
wait.table_name,
wait.table_id_extract waiting_table_id,
wait.need_wait,
wait.SESSION_ID waiting_session_id,                     -- 被阻塞事务的session_id(后端连接ID)
tx.trans_id waiting_trans_id,                           -- 被阻塞事务的ID
tx.ctx_create_time waiting_trx_started,                 -- 被阻塞事务的开启时间
wait.waiting_rowkey,                                    -- 正在尝试加锁的行的主键值
usec_to_time(wait.RECV_TS) waiting_query_started,       -- 被阻塞的SQL开始执行的时间
usec_to_time(wait.LOCK_TS) waiting_last_lock_started,   -- 被阻塞的SQL上一次尝试加锁的时间(10秒重试一次)
wait.TRY_LOCK_TIMES waiting_lock_retry_cnt,             -- 加锁重试次数
wait.TIME_AFTER_RECV/1000 waiting_query_age_ms,         -- 被阻塞的SQL等待锁的时长,单位毫秒
trans.trans_id blocking_trans_id,                       -- 持有锁的事务ID
trans.session_id blocking_session_id,                   -- 持有锁的session_id,即阻塞源
trans.rowkey blocking_rowkey,                           -- 持有锁的行的主键值(防止异常join结果有误,可人工比对)
trans.ctx_create_time blocking_trx_started              -- 持有锁的事务开启时间
FROM __all_virtual_trans_lock_stat trans
JOIN (
    SELECT 
        *,
        SUBSTRING_INDEX(SUBSTRING_INDEX(rowkey, 'table_id=', -1), ' ', 1) AS table_id_extract,
        CONCAT(
            'rowkey_object=[{"%',
            '":"',
            SUBSTRING_INDEX(SUBSTRING_INDEX(rowkey, '":"', -1), '"}]', 1),
            '"}]'
        ) AS waiting_rowkey
FROM gv$lock_wait_stat
) wait
ON trans.table_id = wait.table_id_extract 
AND trans.rowkey LIKE CONCAT('%', wait.waiting_rowkey, '%')
join __all_virtual_trans_stat tx 
on wait.SESSION_ID=tx.session_id\G

输出结果:
2.2 __all_virtual_lock_wait_stat 查看锁等待情况

只有当发生行锁阻塞 __all_virtual_lock_wait_stat 才会有信息:

  • 每行信息表示被阻塞的连接当前正在尝试对哪一行加锁,以及它的后端连接 ID
  • 无法直观看出是被哪个连接阻塞

以如下示例来看:

  • session_id: 3222011615 正在获取 table_id=1100611139453783,rowkey=1 的锁,被阻塞了,阻塞源未知
  • session_id: 3221980123 正在获取table_id=1100611139453783,rowkey=2 的锁,被阻塞了,阻塞源未知
2.3 __all_virtual_trans_lock_stat 查看持有锁的源头

上一步通过 __all_virtual_lock_wait_stat 查看被阻塞的事务正在获取 rowkey=1 这一行的锁,因此接下来通过 __all_virtual_trans_lock_stat 查看持有 rowkey=1 这一行锁的是哪个事务、哪个连接:

  • session_id: 3221980123 持有了 rowkey=1 这一行锁,根据上一步得到的信息,可推测它阻塞了 session_id: 3222011615
  • session_id: 3222011615 持有 rowkey=2 这一行锁,根据上一步得到的信息,可推测它阻塞了 session_id: 3221980123
2.4 kill 锁源头

此处其实是死锁场景,如果要应急解开锁阻塞,需要 kill 阻塞源,也就是持有锁的 session。这里我们选择把 session2 执行的 query 杀掉:

--连2883端口登录业务租户
kill query 3222011615;
2.5 分析业务锁阻塞的原因

通过 gv$sql_audit 可以看到锁阻塞的SQL执行情况:

  • 被锁阻塞的SQL执行时 retry_cnt 很大
  • 被锁阻塞的SQL执行时,elapsed_time 很大,但是 execute_time 很小

要分析业务为什么会有锁阻塞,需要根据事务ID查询 gv$sql_audit 记录的每个事务的所有SQL:

select usec_to_time(request_time), svr_ip, sid, transaction_hash,
user_name, query_sql, ret_code, plan_type, elapsed_time,execute_time, retry_cnt from 
gv$sql_audit where transaction_hash=14355367465281276788 order by 
request_time;

select usec_to_time(request_time), svr_ip, sid, transaction_hash,
user_name, query_sql, ret_code, plan_type, elapsed_time ,execute_time, retry_cnt from 
gv$sql_audit where transaction_hash=12615662878457240999 order by 
request_time;

结果如下:

  • 第一个事务被 kill 回滚了,因此我们只看到一个 SQL
  • 第二个事务与第一个事务都对 id=2 这一行数据加锁,所以发生阻塞,把这个信息给到业务开发,优化业务逻辑

3. 通过日志分析锁阻塞

当没有抓到锁阻塞的现场,但是又想分析业务为什么发生锁等待,可以从 observer.log 来找到锁阻塞的信息。搜索关键字:on_wlock_retry

日志中可能会有很多相关日志,每一次重试加锁都会打印一行日志。可以根据日志中的事务 ID 去gv$sql_audit 中查看事务内容,和业务一起分析原因。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容