《Kingbase護城河》——資料庫卡頓急救手冊:會話狀態深度解析與「殭屍行程」排查實戰

資料庫卡頓急救指南:搞懂會話狀態跟排查「殭屍行程」實戰

在這裡插入圖片描述

在上一篇文章裡面呢,我們其實已經把作業系統的那個障礙給跨過去了,客戶端到伺服器也就算是連通了。那麼問題來了,只要你的資料庫開始跑真實的業務,各種奇怪的毛病往往僅僅只是會跟著跑出來。

通常來說的話,日常維運裡面最讓人頭痛的告警,其實也就是「系統卡頓」了,或者說「資料庫連線池被打滿」的情況。遇到這種緊急的事兒啊,很多新手的直接反應是什麼呢?重啟應用唄,或者更粗暴一點,直接把資料庫服務給重啟了。這其實是不對的。

那麼作為一個搞資料庫維運的人,我們其實得像醫生那樣,透過系統層面的工具去把病因給找出來。這篇文章的話,我就會帶著大家深入到電科金倉資料庫的底層監控台裡面去,手把手教你搞懂系統的會話狀態。也就是說,我們要把那些佔著資源不幹活的長交易,還有殭屍行程給揪出來,接著再把它幹掉。

@[toc]


第一步:開啟全域視角 —— 探秘 sys_stat_activity 檢視表

當業務線的同學跑過來跟你抱怨,說「資料庫沒回應了」的時候。你第一件要做的事,其實絕對不是去翻業務日誌。那該幹嘛呢?也就是說,你得趕緊登入到資料庫裡面去,看看它這個時候到底在忙啥。

在金倉資料庫裡面的話,有一個非常核心的動態系統檢視表,那就是 sys_stat_activity。這個檢視表的話,它其實就像是資料庫的一個監控螢幕一樣,每個連進來的客戶端正在幹啥,它都會即時給你記下來。

1. 發起全盤掃描

那麼你打開本地的終端機,比如 cmd 視窗,接著用 ksql 這個命令列工具去登入資料庫,然後執行下面這段全盤掃描的 SQL 就行了:

sql 体验AI代码助手 代码解读复制代码SELECT 
    pid, 
    usename, 
    client_addr, 
    state, 
    query_start, 
    query 
FROM sys_stat_activity 
WHERE usename IS NOT NULL;

在這裡插入圖片描述

2. 監控指標拆解

那麼我們挑幾個最核心的欄位,用大白話來給大家翻譯一下:

  • pid(程序 ID): 這個的話,其實就是資料庫給這個連線分配的一個作業系統程序號,它是唯一的。比如你截圖裡面看到的那個 20758 啊,還有 12127,你先把它記下來,這可是我們等會兒要去處理的目標。
  • usename(使用者名稱): 這個看名字就知道了,是誰發起的連線。目前顯示的都是 system,也就是說,這是管理員自己人在操作。
  • client_addr(客戶端位址): 發起連線的那台機器的 IP 位址。比如 PID 是 20758 的那一行,它就顯示了具體的客戶端 IP。那如果遇到那種海量的異常連線的情況,你在這裡就能直接看到是哪個網段搞的鬼了。
  • state(目前狀態): 這個欄位的話,可以說是最重要的了!它是判斷你這個連線到底健不健康的唯一標準。在截圖裡面,你可以很清楚地看到 active,也就是活躍的狀態,還有 idle,也就是閒置的狀態。
  • query_start(SQL 開始時間): 這條 SQL 是什麼時候開始跑的?這個東西的話,其實就是我們用來抓那些「長查詢」的一個時間參考點。
  • query(執行語句): 這個程序最後執行的,或者說正在執行的那個 SQL 文本。你注意看截圖裡面,PID 是 20758 的那一行,它的 query 裡面顯示的內容,恰好就是我們剛剛敲進去的這句 SELECT 查詢語句本身!這也就證明了,我們的監控是即時在跑的。

第二步:解碼 state —— 揪出真正搞鬼的傢伙

監控螢幕上那些密密麻麻的程序,其實並不全都是壞東西。那麼在 sys_stat_activitystate 欄位裡面的話,我們通常來說需要學會去分辨下面這幾種核心的狀態:

1. active(正在執行)

也就是說,這個連線它現在正在那老老實實地跑 query 欄位裡面的 SQL 語句,就像截圖裡那個 PID 20758 那樣。

  • 診斷: 如果這個查詢才剛開始幾秒鐘的情況,那是很正常的。但如果一個 active 狀態的 SQL 跑了幾十分鐘還沒完,那就是典型的「慢查詢」了。它在瘋狂吃你的 CPU 和 IO 資源,這時候你就得去看看它的執行計畫了。

2. idle(閒置)

這個狀態的話,意思是客戶端連著資料庫,但是目前啥活都沒幹,就像截圖裡面那個 PID 12127 那樣。

  • 診斷: 這是極其正常的。因為 Java 這些後端應用的話,通常來說它們都用了「資料庫連線池」,就像 HikariCP 或者 Druid 那種。也就是說,為了避免頻繁建連帶來的開銷,應用會保持一批 idle 狀態的長連線在那隨時待命。只要總連線數沒爆滿的情況,你就不用去管它。

3. idle in transaction(交易中閒置)—— ⚠️ 核心警報!

這個的話,就是我們常說的那種佔著資源不幹活的典型代表了!它的意思是,客戶端開了一個交易,可能執行了 BEGIN 或者關了自動提交,接著跑了幾條 SQL 之後,它既沒有 COMMIT,也沒有 ROLLBACK,就這麼一直掛在那邊了。

  • 危害: 這玩意可能會拿著很關鍵的表鎖或者列鎖不放。這就導致別的想要改這些資料的業務,都被卡在那裡動不了了。而且的話,它還會阻礙資料庫底層的垃圾回收,那個表就會變得很大。所以啊,這個在正式環境裡面,是我們必須重點去幹掉的「殭屍行程」。

實戰小結:一鍵抓取異常會話的 SQL

日常巡檢的時候呢,我們其實不需要像剛才那樣看滿屏的資料。你要幹的僅僅只是把執行時間超過 1 分鐘的 active 語句給抓出來,還有把所有的 idle in transaction 程序給找出來就行。那麼你可以把下面這段 SQL 收藏起來,當成你的 DBA 必殺技:

sql 体验AI代码助手 代码解读复制代码SELECT 
    pid, 
    usename, 
    client_addr, 
    state, 
    now() - query_start AS duration,
    query 
FROM sys_stat_activity 
WHERE state IN ('active', 'idle in transaction') 
  AND state IS NOT NULL 
  AND (now() - query_start) > interval '1 minute';

第三步:現場模擬 —— 親手製造一個殭屍行程

光看理論其實沒用,既然手頭有 ksql 環境,那麼我們就可以自己來造一個 idle in transaction 的案發現場看看。

  1. 先打開第一個 cmd 視窗進去 ksql(這就相當於模擬那個出 Bug 的業務系統):建一張測試表,接著開一個交易,去執行一下更新操作,但是記住千萬別提交!
sql 体验AI代码助手 代码解读复制代码-- 建表並插入一筆資料
CREATE TABLE test_lock (id int, name varchar(50));
INSERT INTO test_lock VALUES (1, '金倉護航者');

-- 開啟交易,執行更新,但不做 COMMIT!
BEGIN;
UPDATE test_lock SET name = '殭屍資料' WHERE id = 1;
  1. 接著打開第二個 cmd 視窗進去 ksql(這就相當於我們 DBA 巡檢的視角了):在這裡面,我們再去執行一下前面說的那個全盤掃描 SQL,或者說你那個 DBA 必殺技 SQL 也行。

在這裡插入圖片描述

那麼你在右側的這個 DBA 監控視圖裡面,就能看得很清楚了。PID 是 20758 的那個連線,正是因為我們在左邊敲了 BEGIN 並且沒提交,所以這個時候它的 state 就變成了那個讓人頭痛的 idle in transaction 了!而且的話,它最後執行的那句 UPDATE 語句,也被我們監控視圖給扒出來了。


第四步:動手清理 —— 優雅清理異常會話

既然抓到了搞鬼的傢伙,也就是那個 PID: 20758,接著就是去處理它了。

這裡一定要記住:千萬別去 CentOS 作業系統的終端機裡面,用 kill -9 去殺資料庫的實體程序! 這麼搞的話,極容易把共享記憶體給搞壞,甚至會讓整個資料庫執行個體直接當掉重啟的。

金倉資料庫它自己在內部就提供了一個很好用的內建函式,那就是:sys_terminate_backend(pid)

那麼在你右側的這個 DBA 監控視窗裡面,對著剛才找到的那個殭屍行程 PID 20758,去執行下面這個強制切斷的命令:

sql 体验AI代码助手 代码解读复制代码-- 這裡的 20758 就是我們在監控視圖中抓到的異常程序 PID
SELECT sys_terminate_backend(20758);

執行完了之後,它會返回一個 t (true),這就說明處理成功了。 在這裡插入圖片描述

到了這個時候,那個被卡住的鎖就已經被資料庫給強制放開了,沒提交的髒資料也會自動回滾掉。那之前被卡住的其他業務,往往僅僅只是瞬間就能恢復順暢了。


終極防禦:給系統設定一個超時斷點防身

手動殺程序的話雖然很爽,但是 DBA 也不能天天盯著螢幕當監工對吧。如果業務線新來的開發同學程式碼寫得不行,經常忘了寫 commit 的情況,那該怎麼辦呢?

其實我們可以從資料庫底層下手,去設一個全域的防線:也就是交易閒置超時自動斷開

你去打開伺服器端的資料目錄,接著修改 kingbase.conf 這個設定檔就行:

ini 体验AI代码助手 代码解读复制代码# 找到或手動新增該參數(單位為毫秒)
# 例如設定為 60000(即 60 秒),表示只要交易處於 idle in transaction 狀態超過 60 秒,資料庫直接幫你把這個會話踢掉。
idle_in_transaction_session_timeout = 60000

(註:改完了之後,記得在 Linux 終端機去執行一下 sys_ctl reload ,讓設定熱載入生效哈)

配了這個參數之後的話,也就相當於給所有的資料庫連線加了個倒數計時,再也不用怕寫得爛的程式碼把整個資料庫執行個體給拖垮了。

結語

那麼透過上面對 sys_stat_activity 的拆解和實作,我們其實也就掌握了資料庫效能排障的第一項核心技能,也就是會話狀態的判斷和精準干預。

但是在真實的複雜業務裡面的話,卡頓往往不僅僅只是「忘了提交」這麼簡單。有時候啊,幾個正常的業務程序,它們會因為搶同一張表的修改權而互相卡死,這就變成了那種很隱蔽的「列鎖等待」了。

在下一篇文章裡面呢,我們會接著往資料庫的微觀世界裡面走,手把手教你去搞定那種更複雜的 「列鎖衝突與等待事件」。也就是說,讓你真正具備從根源上診斷,還有優化高併發業務的能力。大家就期待一下吧!


原文出處:https://juejin.cn/post/7649298814721900578


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝7   💬1  
353
🥈
我愛JS
💬1  
3
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登