基於日誌或 gv$sql_audit 分析 OB 異常重試 SQL

本文以 SQL 異常重試場景爲例,使用基於 日誌文件 和 gv$sql_aduit 視圖 這兩種方式,找出具體的報錯原因。

作者:鄭增權,愛可生 DBA 團隊成員,OceanBase 和 MySQL 數據庫技術愛好者。

愛可生開源社區出品,原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。

本文約 2000 字,預計閱讀需要 8 分鐘。

簡介

我們在 OCP 雲平臺 Top SQL 界面看到一些異常 SQL,但並未提示具體報錯原因或提示了原因但不夠詳細。

本文以 SQL 異常重試場景爲例,使用基於 日誌文件gv$sql_aduit 視圖 這兩種方式,找出具體的報錯原因。

本文更推薦 PC 端瀏覽~

背景

  1. OceanBase 3.X 企業版 MySQL 模式
  2. 某客戶在性能壓測過程中反饋,在對某張表 UPDATE 時響應緩慢,一直無法執行成功。
  3. gv$sql_aduit 關於此 SQL 相關信息已被清理,且 Top SQL 未提示報錯具體原因,只能基於日誌文件進行排查。
  4. OCP 雲平臺查看初始狀態如下:
    • UPDATE 語句重試 440 次
    • 平均響應時間爲 5491.15 ms

  1. 文中後半部分對此場景進行延伸。
  • 假設 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 文本無法精準匹配,則只複製部分關鍵字。
  • 可以看到 40126003 等超時相關錯誤碼。
  • 複製一條 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,說明是符合預期的。下面來驗證一下。

  1. 查詢租戶變量 ob_query_timeout 爲 10s。

  1. 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 報錯(語句超時)回滾語句。

可能造成此問題的原因:

  1. 業務使用了較大的超時時間,且存在一個會話中的未知長事務持有鎖,阻塞了其他事務的執行。
  2. 開發人員併發更新同一行數據,併發處理邏輯存在錯誤。

優化措施

  1. 合理設置超時變量時間。
  2. 合理設置程序代碼併發控制邏輯。
  3. 關注長事務告警。

延伸場景

如果 SQL 正在持續重試中,且 gv$sql_audit 視圖信息未被清除,可參考如下步驟進行排查。

1. OCP 雲平臺,複製 SQL ID

2. 基於 SQL ID 查看主要的錯誤代碼

可以看到 40126003 等超時相關錯誤碼。

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_idgv$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_zhengid=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 狀態。

參考資料

  1. 《OceanBase 數據庫日誌解讀示例》https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000000207691
  2. 《OceanBase 數據庫事務問題排查指南》https://www.oceanbase.com/knowledge-base/oceanbase-database-20000000026
  3. 《OceanBase 數據庫中的行鎖問題排查指南》https://www.oceanbase.com/knowledge-base/oceanbase-database-20000000016
  4. 《事務控制概述》https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000642694

更多技術文章,請訪問:https://opensource.actionsky.com/

關於 SQLE

SQLE 是一款全方位的 SQL 質量管理平臺,覆蓋開發至生產環境的 SQL 審覈和管理。支持主流的開源、商業、國產數據庫,爲開發和運維提供流程自動化能力,提升上線效率,提高數據質量。

SQLE 獲取

類型 地址
版本庫 https://github.com/actiontech/sqle
文檔 https://actiontech.github.io/sqle-docs/
發佈信息 https://github.com/actiontech/sqle/releases
數據審覈插件開發文檔 https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章