本文以 SQL 異常重試場景爲例,使用基於 日誌文件 和 gv$sql_aduit 視圖 這兩種方式,找出具體的報錯原因。
作者:鄭增權,愛可生 DBA 團隊成員,OceanBase 和 MySQL 數據庫技術愛好者。
愛可生開源社區出品,原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。
本文約 2000 字,預計閱讀需要 8 分鐘。
簡介
我們在 OCP 雲平臺 Top SQL 界面看到一些異常 SQL,但並未提示具體報錯原因或提示了原因但不夠詳細。
本文以 SQL 異常重試場景爲例,使用基於 日誌文件 和 gv$sql_aduit
視圖 這兩種方式,找出具體的報錯原因。
本文更推薦 PC 端瀏覽~
背景
- OceanBase 3.X 企業版 MySQL 模式
- 某客戶在性能壓測過程中反饋,在對某張表 UPDATE 時響應緩慢,一直無法執行成功。
gv$sql_aduit
關於此 SQL 相關信息已被清理,且 Top SQL 未提示報錯具體原因,只能基於日誌文件進行排查。- OCP 雲平臺查看初始狀態如下:
- UPDATE 語句重試 440 次
- 平均響應時間爲 5491.15 ms
- 文中後半部分對此場景進行延伸。
- 假設 SQL 正處於異常重試狀態中,且關聯的
gv$sql_audit
視圖信息未被清除的情況下,如何展開排查提供思路。
排查過程
1. 導出 Top SQL
列管理 按需勾選需查看的信息(如:SQL ID,重試次數)。
2. 複製 SQL 文本
3. 查找 UPDATE 語句
在對應的服務器上 grep
此 SQL 語句的打印次數:
- 結果爲 1 小時內執行了 505 次,判斷針對該行數據的 UPDATE 行爲可能存在異常。
- 日誌打印 SQL 次數可能與 Top SQL 不同,大致對應上即可。
- 一般異常 SQL 會在
observer.log
中打印,正常執行完成的可能不會存在記錄。
# grep -i "UPDATE evan.evan_zheng SET name = 'test0409' WHERE id = 1" observer.log.2024040916* | wc -l
505
4. 查找錯誤位置
以 SQL 語句 和 ret=
作爲條件進行檢索,看是否存在相關錯誤碼。
- 若 SQL 文本無法精準匹配,則只複製部分關鍵字。
- 可以看到 4012,6003 等超時相關錯誤碼。
- 複製一條
trace id
用於檢索
# grep -i "UPDATE evan.evan_zheng SET name = 'test0409' WHERE id = 1" observer.log.2024040916* | grep "ret="
5. 查看報錯信息
檢索 trace_id
,查看主要報錯信息。
- 寫寫衝突:on_wlock_retry、lock_for_write conflict
- 錯誤碼:6005
- 更新某行數據失敗:failed to update row (xxx)
6. 寫寫衝突部分日誌
# grep -i "YB420ABA40A1-000615A29EDEEA36-0-0" observer.log.2024040916* | grep "lock_for_write conflict"
7. 確認行爲
確認此 trace_id
關聯的 SQL 存在重試行爲。
# grep -i "YB420ABA40A1-000615A29EDEEA36-0-0" observer.log.2024040916* | grep -i "will retry"
8. 錯誤碼含義
錯誤碼 6005:更新操作加鎖失敗
錯誤碼 6003:等待鎖超時
錯誤碼 6212:SQL 語句超時
對於語句超時的情況,首先要確定當前租戶下 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
視圖信息未被清除,可參考如下步驟進行排查。
1. OCP 雲平臺,複製 SQL ID
2. 基於 SQL ID 查看主要的錯誤代碼
可以看到 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;
3. 查看 session_id
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;
4. 查詢 table_id
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';
5. 查詢鎖持有者事務信息
使用 sys
租戶執行。
select * from __all_virtual_trans_lock_stat where table_id = '1100611139453778'\G
6. 查詢鎖等待者事務信息
使用 sys
租戶執行。
可以看到此處 session_id
與 gv$sql_audit
查詢出來的是一致的(即,異常重試的 SQL 的會話)。
select * from __all_virtual_lock_wait_stat where table_id = '1100611139453778'\G
7. 查詢鎖持有者 session 的 SQL
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;
8. 查看鎖等待者 session 的 SQL
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 操作。
9. KILL 鎖持有者會話
解決方法:經確認風險後,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 數據庫日誌解讀示例》https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000000207691
- 《OceanBase 數據庫事務問題排查指南》https://www.oceanbase.com/knowledge-base/oceanbase-database-20000000026
- 《OceanBase 數據庫中的行鎖問題排查指南》https://www.oceanbase.com/knowledge-base/oceanbase-database-20000000016
- 《事務控制概述》https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000642694
更多技術文章,請訪問:https://opensource.actionsky.com/
關於 SQLE
SQLE 是一款全方位的 SQL 質量管理平臺,覆蓋開發至生產環境的 SQL 審覈和管理。支持主流的開源、商業、國產數據庫,爲開發和運維提供流程自動化能力,提升上線效率,提高數據質量。