作者:郑增权,爱可生 DBA 团队成员,OceanBase 和 MySQL 数据库技术爱好者。
爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
本文约 2000 字,预计阅读需要 8 分钟。
我们在 OCP 云平台 Top SQL 界面看到一些异常 SQL,但并未提示具体报错原因或提示了原因但不够详细。
本文以 SQL 异常重试场景为例,使用基于 日志文件 和 gv$sql_aduit
视图 这两种方式,找出具体的报错原因。
本文更推荐 PC 端浏览~
gv$sql_aduit
关于此 SQL 相关信息已被清理,且 Top SQL 未提示报错具体原因,只能基于日志文件进行排查。gv$sql_audit
视图信息未被清除的情况下,如何展开排查提供思路。列管理 按需勾选需查看的信息(如:SQL ID,重试次数)。
在对应的服务器上 grep
此 SQL 语句的打印次数:
observer.log
中打印,正常执行完成的可能不会存在记录。# grep -i "UPDATE evan.evan_zheng SET name = 'test0409' WHERE id = 1" observer.log.2024040916* | wc -l
505
以 SQL 语句 和 ret=
作为条件进行检索,看是否存在相关错误码。
trace id
用于检索# grep -i "UPDATE evan.evan_zheng SET name = 'test0409' WHERE id = 1" observer.log.2024040916* | grep "ret="
检索 trace_id
,查看主要报错信息。
# grep -i "YB420ABA40A1-000615A29EDEEA36-0-0" observer.log.2024040916* | grep "lock_for_write conflict"
点击放大
确认此 trace_id
关联的 SQL 存在重试行为。
# grep -i "YB420ABA40A1-000615A29EDEEA36-0-0" observer.log.2024040916* | grep -i "will retry"
对于语句超时的情况,首先要确定当前租户下 ob_query_timeout
变量设置,然后根据 trace_id
搜索 observer.log
日志,找到当前语句的 cur_query_start_time
。
如果 超时时间点 - cur_query_start_time
= ob_query_timeout
,说明是符合预期的。下面来验证一下。
ob_query_timeout
为 10s。observer.log
中检索此 trace_id
的起始时间。开始时间:
# grep -i "YB420ABA40A1-000615A29EDEEA36-0-0" observer.log.20240409* | grep -m 1 "cur_query_start_time"
超时时间:
# grep -i "YB420ABA40A1-000615A29EDEEA36-0-0" observer.log.20240409* | grep "timeout_timestamp" | tail -n 1
可以看到超时时间减去开始时间等于 10s,说明此处超时行为符合预期。
当执行 SQL UPDATE evan.evan_zheng SET name = 'test0409' WHERE id = 1;
更新操作加锁失败,达到当前租户 ob_query_timeout
变量设置的值(10s)触发 6212 报错(语句超时)回滚语句。
可能造成此问题的原因:
如果 SQL 正在持续重试中,且 gv$sql_audit
视图信息未被清除,可参考如下步骤进行排查。
可以看到 4012,6003 等超时相关错误码。
select
/*+ PARALLEL(8)*/
trace_id,
sid,
tenant_name,
svr_ip,
svr_port,
retry_cnt,
ret_code,
query_sql,
usec_to_time(request_time) as start_time
from
gv$sql_audit
where
sql_id = 'D884EA797E73F466819BAE2AE4AC1FE1'
and retry_cnt > 1
group by
ret_code
order by
retry_cnt desc;
select
/*+ PARALLEL(8)*/
trace_id,
sid,
tenant_name,
svr_ip,
svr_port,
retry_cnt,
ret_code,
query_sql,
usec_to_time(request_time) as start_time
from
gv$sql_audit
where
sql_id = 'D884EA797E73F466819BAE2AE4AC1FE1'
group by
sid
order by
request_time desc;
select
database_name,
table_id,
table_name,
tenant_id,
tenant_name
from
oceanbase.gv$table
where
tenant_id = 1001
and database_name = 'evan'
and table_name = 'evan_zheng';
使用 sys
租户执行。
select * from __all_virtual_trans_lock_stat where table_id = '1100611139453778'\G
使用 sys
租户执行。
可以看到此处 session_id
与 gv$sql_audit
查询出来的是一致的(即,异常重试的 SQL 的会话)。
select * from __all_virtual_lock_wait_stat where table_id = '1100611139453778'\G
select
trace_id,
usec_to_time(request_time),
query_sql
from
gv$sql_audit
where
TENANT_ID = 1001
AND USER_NAME = 'root'
AND SID = '3221616444'
order by
request_time desc;
select
trace_id,
usec_to_time(request_time),
query_sql
from
gv$sql_audit
where
TENANT_ID = 1001
AND USER_NAME = 'root'
AND SID = ' 3221618060'
order by
request_time desc;
可以看到锁持有者的会话和锁等待者的会话都针对表 evan_zheng
中 id=1
的字段进行更新,由于锁持有者开启了手动提交且未进行提交导致锁等待者持续重试 UPDATE 操作。
解决方法:经确认风险后,kill 锁持有者会话。
进一步分析可参考前方步骤,结合 observer.log
等信息进行分析。
通过如下几个错误码可以判断 SQL 超时原因(语句超时/事务超时/事务空闲超时):
ob_query_timeout
: 该变量控制着语句执行时间的上限,语句执行时间超过此值会给应用返回语句超时的错误,错误码为 6212,并回滚语句,通常该值默认为 10s。ob_trx_timeout
: 该变量控制着事务超时时间,事务执行时间超过此值会给应用返回事务超时的错误,错误码为 6210,此时需要应用发起 ROLLBACK 语句回滚该事务。ob_trx_idle_timeout
: 该变量表示 session上一个事务处于的 IDLE 状态的最长时间,即长时间没有 DML 语句或结束该事务。超过该时间值后,事务会自动回滚。再执行 DML 语句会给应用返回错误码 6224,应用需要发起 ROLLBACK 语句清理 session 状态。本文关键字:#OceanBase# #事务# #故障分析# #SQL#