作為一名混跡數(shù)據(jù)庫江湖十幾年的老 DBA,當(dāng)你對(duì)關(guān)系型數(shù)據(jù)庫的了解越來越深入時(shí),你會(huì)發(fā)現(xiàn),ORACLE 數(shù)據(jù)庫真的是強(qiáng)大到令人發(fā)紫!
Oracle 數(shù)據(jù)庫的強(qiáng)大,不僅體現(xiàn)在其對(duì) ACID 的巧妙實(shí)現(xiàn),其對(duì)高并發(fā)的完美支持,更重要的是他的可管理性,包括可度量、可回溯,以及出現(xiàn)問題后的問題核查接口和問題檢查方法論,真是強(qiáng)大到令人發(fā)紫, 這是其他關(guān)系型數(shù)據(jù)庫短期內(nèi)還無法超越的。
問題來了!?。?!
電話響了,是某銀行一位熟悉的資深 DBA 的來電。
“在嗎?現(xiàn)在應(yīng)用連接數(shù)據(jù)庫會(huì) hang 住,sysdba 登陸也會(huì) hang 住,無報(bào)錯(cuò),該如何處理?”
沒有往日的寒暄和客套,直入主題!
人的聲音是有表情的,從電話那頭急促的語氣,不難判斷,客戶很著急。
可能有些朋友不清楚數(shù)據(jù)庫登錄 hang 住是怎樣的一種現(xiàn)象,下圖可以腦補(bǔ)一下:
也就是說,正常的登錄是可以快速看到 “SQL>” 這樣的提示符的,但出現(xiàn)異常時(shí),就會(huì)長時(shí)間等不到 “SQL>” 這樣的提示符, 這就是所謂的登錄數(shù)據(jù)庫會(huì) hang 住。
看到這里,有些朋友開始激動(dòng)了,要猜一下原因,試一下身手!
1)是不是數(shù)據(jù)庫歸檔滿了?
答:這… 歸檔滿了,sysdba 登錄會(huì)報(bào) ORA - 歸檔錯(cuò)誤相關(guān)的提示!而且注意細(xì)節(jié),之前提到了,客戶是資深的 DBA,顯然這種可能性早就被排除掉了, 注意細(xì)節(jié)啊 ^_^
2)查一下等待事件,看看在等什么呢?
答:這… 數(shù)據(jù)庫都連不進(jìn)去了,怎么發(fā)出 SQL 來查詢呢…
3)alert 日志有什么明顯報(bào)錯(cuò)么?
答:在這個(gè) case 中 alert 日志沒有報(bào)錯(cuò),也沒有明顯問題…
三板斧用完后,接下來不妨思考個(gè)兩三分鐘,如果是你,接下來你要怎么指揮這場(chǎng)戰(zhàn)斗…
“別著急,你收兩個(gè) SSD 保存現(xiàn)場(chǎng),然后殺掉 pmon,先恢復(fù)業(yè)務(wù),然后把 SSD 的 trace 發(fā)我,我來做下 RCA!”
客戶殺完 pmon 進(jìn)程,數(shù)據(jù)庫自動(dòng)重啟后,業(yè)務(wù)恢復(fù)正常。隨后將 SSD 發(fā)了過來。
這里有些同學(xué)聽到這些術(shù)語,有些摸不著頭腦了:
什么是 SSD?固態(tài)盤(不會(huì)吧)?
還有什么是 RCA 呢?
這里給大家科普一下:
SSD 其實(shí)就是 System State Dump, 系統(tǒng)即時(shí)狀態(tài) DUMP 的首字母組合,
RCA 就是 Root Cause Analyze, 根因分析,是解決問題的難度要大許多,也有意思許多
為什么要收集 SSD 呢?
因?yàn)樵虻牟淮_定性,怎么能抓到蝴蝶效應(yīng)中的那只蝴蝶呢?那就需要足夠的信息!
多年前未掌握 SSD 這個(gè)功能的時(shí)候,出現(xiàn)問題,喜歡收集 v$session,v$session_wait,v$sqlarea,v$lock 等動(dòng)態(tài)性能的相關(guān)信息,然后重啟,但是后來往下分析的時(shí)候,發(fā)現(xiàn)少收集了什么信息,導(dǎo)致分析不順利,后悔莫及…
當(dāng)時(shí)就在想,Oracle 是否有一個(gè)一鍵收集的功能:
把想要的,不想要的,全都收集下來呢!答案就是 SSD。
甚至是當(dāng) sysdba 無法登陸時(shí),Oracle 依然可以直接 attach 到共享內(nèi)存,將內(nèi)存中的即時(shí)狀態(tài)全部抓取下來,包括系統(tǒng)當(dāng)前各個(gè)進(jìn)程正在執(zhí)行什么、正在等什么、進(jìn)城的堆棧等信息,真是強(qiáng)大大令人發(fā)紫的一個(gè)功能。
SSD 的收集非常簡單,照敲就是了,以下是 SSD 收集的命令
### sqlplus -prelim "/as sysdba"
SQL>oradebug setmypid
SQL>oradebug dump systamstate 266
SQL>-- 等上 30 秒到 1 分鐘
SQL>oradebug dump systamstate 266
SQL>oradebug tracefile_name
接下來就帶領(lǐng)大家一起去分析 SSD,做根因分析,你會(huì)發(fā)現(xiàn)工作是一件多么有趣的事情。
1. 查看登陸進(jìn)程在等什么
從 xxdb_ora_33030248.trc 中搜索 "waiting for" 可以看到:
可以看到:
1)有N 個(gè)進(jìn)程都在等 LATCH:librarycache,latch, 并且 latch 是同一個(gè)即 70000006b9d8008
2) 1個(gè)進(jìn)程在等 cursor:pin X,即在等待 cursor 類型的 mutex
3) latch 等待的時(shí)間已經(jīng)長達(dá)達(dá)到 3723 秒
這里不難看出:
由于登陸的時(shí)候,要執(zhí)行包括驗(yàn)證用戶、獲取權(quán)限等內(nèi)部的 SQL(遞歸 SQL),但是在發(fā)出 SQL 后,由于長時(shí)間無法獲取 latch:library cache 這樣的資源,因此登陸看上去就像 hang 住了一樣… 接下來,我們只需要找到無法 latch:library cache 的原因,就可以解開數(shù)據(jù)庫 hang 住的真相了!
2. 第一次頭腦風(fēng)暴
看到這里,也許有同學(xué)迫不及待地又想再試試身手:
是不是硬解析的問題?
可以看到:
當(dāng)客戶端發(fā)出的 SQL 到達(dá)數(shù)據(jù)庫的服務(wù)進(jìn)程后,要先在 shared pool 中去找內(nèi)存中是否存在該 SQL 和執(zhí)行計(jì)劃,如果存在則拿到執(zhí)行計(jì)劃直接執(zhí)行即可。
那么 oracle 是如何查找的呢?就是對(duì) SQL 文本計(jì)算 hash 值后,獲取 latch:library cache(11g 中則采用 mutex 代替),對(duì)對(duì)應(yīng)的鏈表進(jìn)行掃描即可。
因此,軟解析也會(huì)申請(qǐng)?jiān)?latch。
所以,不能說是簡單的硬解析的問題,一切都有可能 。
BTW, 筆者面試過很多人,其實(shí)更想看到的是分析問題的方法論,而不是使勁的猜…
為什么呢?我們總會(huì)遇到很多經(jīng)驗(yàn)范圍之外的事情,怎么可能猜出自己不知道的事情呢?
3. 找原因,Orale 就是這么簡單!
既然長時(shí)間無法獲取 latch, 那么是誰在持有 latch 呢?
需要說明的是,當(dāng)無法獲取 latch:library cache 的時(shí)候,Oracle 在實(shí)現(xiàn)上,會(huì)將自己放到 latch 的等待者列表 waiter list 當(dāng)中,那么自然也就有一個(gè)對(duì)應(yīng)的持有者列表,
這么做的原因在于,當(dāng)持有者使用完該 latch 后,到等待者列表中喚醒等待的進(jìn)程即可。同時(shí),Oracle 在做 SSD 的時(shí)候,就已經(jīng)把持有者給打印到 trace 里了。
搜索 "waiting for 70000006b9d8008 Childlibrary"
可以看到 “possible holder pid = 19ospid=10027060”,即持有者是 pid = 19 ospid=10027060
接下來,我們需要去看看 latch 持有者即 pid = 19 ospid=10027060 的進(jìn)程在做什么
4. 持有 latch 的人去哪了?
搜索 “ospid:10027060”,就可以看到 LATCH 持有者的進(jìn)程的詳細(xì)信息了
包括進(jìn)程名,在執(zhí)行什么 SQL,進(jìn)程狀態(tài)是什么,在等什么資源…
可以看到:
Pid=19,spid=10027060 的進(jìn)程,是 ORACLE 的一個(gè) JOB SLVAE 進(jìn)程 j001,
由于他在持有 latch, 導(dǎo)致了很多進(jìn)程需要等待,
holding (efd=5) 70000006b9d8008 Child library cache
乘勝追擊,進(jìn)一步查看該進(jìn)程在等什么資源:
可以看到:
該進(jìn)程對(duì)應(yīng)的 SID 是 534,當(dāng)前實(shí)際上并沒有在等待任何資源,因?yàn)?last wait 表示的是上一次的等待了。長時(shí)間持有 latch:library cache, 導(dǎo)致 N 個(gè)進(jìn)程登陸執(zhí)行內(nèi)部 SQL 的時(shí)候無法獲取 latch, 繼而無法登陸,但是,進(jìn)程持有者 PID=19,SID=534,又沒有在等待任何資源,SQL:0 表示當(dāng)前沒有在執(zhí)行任何 SQL。
生無可戀了,那我怎么知道進(jìn)程持有者在做什么呢,這還怎么往下查呢…
提示:這里請(qǐng)記住 latch 的持有者,SID 是 534,534!
5.陷入僵局
還記得么,Oracle 有一套方法論,那么方法論就是查看 call stack, 通過查看進(jìn)程調(diào)用的函數(shù)軌跡,就可以判斷出來,當(dāng)前進(jìn)入了哪一種場(chǎng)景。
但是由于客戶一著急,收集的 SSD 的 level 不夠,因?yàn)闆]有打印每個(gè)進(jìn)程的 call stack!
這可如何是好啊, 難道問題要陷入僵局..
如果是你,接下來,會(huì)怎么往下打這一場(chǎng)仗
6.細(xì)節(jié)決定成敗
如圖所示:
紅色加框部分顯示,該進(jìn)程的狀態(tài)處于 DEAD 狀態(tài)!即持有 latch 的那個(gè)進(jìn)程已經(jīng)死掉了!
看到這里:
有些朋友又要蒙圈了,“這是什么情況?”
有些朋友可能已經(jīng)開始有點(diǎn)想法了,心里在嘿嘿樂…
沒錯(cuò),實(shí)際上,這已經(jīng)設(shè)計(jì)到道和術(shù)的問題。
技術(shù)層面上,一路找到最終的阻塞者后,已經(jīng)進(jìn)行不下去了!
接下來,大家不妨停下來,思考一下:
原理層面呢?
學(xué)了那么多體系架構(gòu)的東西,怎么用到生產(chǎn)問題中呢?
是否可以運(yùn)用原理幫助解開這個(gè)數(shù)據(jù)庫掛起的問題呢?
我面試候選 DBA 的時(shí)候,喜歡問原理。
很多候選 DBA 答不上來的時(shí)候,總喜歡解釋道,而且是很坦然的解釋到:
不好意思,過去從來不關(guān)注原理, 熟練操作就可以了!
聽到這些回答,本人總會(huì)語重心長的讓對(duì)方做一道故障題,不掌握原理是不可能解開的,結(jié)果很顯然的,候選人自然答不上來,之后我會(huì)演示問題處理和分析過程,候選人往往都會(huì)重新定義對(duì)道和術(shù)的認(rèn)知,孺子可教...
工程師熟練操作是基礎(chǔ),,但是從中級(jí)工程師到高級(jí)工程師,再到資深工程師,深入原理是一道坎,能將原理熟練應(yīng)用到實(shí)際分析中又是一道坎。什么時(shí)候跨過坎了,層次也就不一樣了。很多 DBA 因?yàn)闆]有人點(diǎn)撥,可能永遠(yuǎn)過不了那道坎…
7. 振聾發(fā)聵的一問!
為什么進(jìn)程死掉了,但是進(jìn)程還在持有 latch 資源不釋放?
PMON 做什么去了?他是干什么吃的…
是的!這就是問題的關(guān)鍵!當(dāng)聽到這么一個(gè)振聾發(fā)聵的驚天一問時(shí),恭喜你,跨過了一道坎!
如果已經(jīng)提示到這個(gè)程度,依然無法發(fā)出這么一個(gè)疑問,實(shí)在是!
8. 看看 PMON 在做什么
搜索(PMON),就可以找到 SSD 中 PMON 進(jìn)程的相關(guān)信息。如下所示:
可以看到:
PMON 正在等待 cursor:pin x,即申請(qǐng)模式為獨(dú)占,類型為 cursor 的 mutex
waiting for 'cursor: pin X'
該 mutux 的 IDN 是 idn=ad39e34, 即 hash 值
由于 PMON 被阻塞, 卡住了,因此自然沒有機(jī)會(huì)去清理死去進(jìn)程所持有的 LATCH 了!
我們繼續(xù)真相又進(jìn)了一步!
只需要集中精力,需要繼續(xù)到底是是哪個(gè)進(jìn)程,持有了 idn=ad39e34 的 mutex, 導(dǎo)致 PMON 被長時(shí)間阻塞了,就可以解開問題的真相了!
接下來,大家不妨停下來,思考一下:
上圖中, BLOCKING_SESS=0X0,這里無法直接查看是誰阻塞了 PMON 進(jìn)程。
那么如果是你,你會(huì)怎么往下查呢
9. 誰阻塞了 PMON
由于 PMON 進(jìn)程以獨(dú)占方式申請(qǐng)
類型為 cursor 的 mutex 被阻塞,顯然該 MUTEX 正在被某個(gè)進(jìn)程以獨(dú)享或獨(dú)占方式長時(shí)間持有。這顯然是不正常的。畢竟 MUTEX 是一種輕量級(jí)的資源。
接下來,我們?cè)?TRACE 中搜索 "idn ad39e34 oper",結(jié)果如下所示
Mutex 70000003eec4be0(534, 0) idn ad39e34 oper GET_EXCL
Mutex 70000003eec4be0(534, 0) idn ad39e34 oper EXCL
可以看到:
該 MUTEX 上有兩個(gè)操作, OPER 即 Operation, 操作。
一個(gè)進(jìn)程正在以獨(dú)占方式持有, 即 oper EXCL
另外一個(gè)進(jìn)程正以獨(dú)占方式申請(qǐng),oper GET_EXCL,Get 表示申請(qǐng), 因此發(fā)生阻塞。該進(jìn)程就是 PMON 進(jìn)程。
紅色底紋部分的 534,就表示 MUTEX 的持有者,即 SID=534!
沒錯(cuò)!SID=534 就是我們之前持有 latch:library cache 資源但已經(jīng)死去的進(jìn)程!
就是那個(gè)等著被 PMON 清理的死去的進(jìn)程!
10. 分析總結(jié)
綜合上述分析,總結(jié)如下:
1) N 個(gè)進(jìn)程無法登陸,是因?yàn)闊o法獲得 latch:library cache 資源,該資源被一個(gè)死去的 SID=534 的進(jìn)程持有了, 還沒釋放!
2) 按照原理,PMON 有義務(wù)去清理死去的 SID=534 的進(jìn)程所持有的資源(latch 等).
3) 但是 PMON 只有一個(gè),PMON 正在等'cursor:pin X', 即以獨(dú)占方式申請(qǐng)類型為 cursor 的 mutex. 所以騰不出手來清理死去的 SID=534 的進(jìn)程.
4) 正是 SID=534 持有 MUTEX,阻塞了 PMON !
假設(shè)說步驟 1,2 還合理的話,但是步驟 3 和 4 就毀三觀了!
總結(jié)起來就一句話,PMON 要去給死去的進(jìn)程收尸,但是要獲得死去進(jìn)程的同意!
這太不合理,太不科學(xué)了!為什么會(huì)這樣呢…
很簡單,命中 BUG!
11. 輕松找 BUG
分析到這里,掌握了問題的本質(zhì),那么找 BUG 起來就很簡單了!
ORACLE 有一個(gè)強(qiáng)大的知識(shí)庫,記錄了全球客戶提交過的 CASE,里面包含了 BUG 庫!
怎么找到具體的 BUG 呢?
接下來不妨思考個(gè) 1 分鐘,如果是你,接下來你要怎么定搜索關(guān)鍵字呢…
這里,以 “pmon cursor dead” 做為關(guān)鍵字(其他關(guān)鍵字也可以),檢索 BUG。
很快,一個(gè) BUG 的標(biāo)題引起了注意:
Bug 8426816 PMON may hang cleaning up a dead process (rare)
點(diǎn)開 BUG,描述如下:
怎么樣,看完了吧,這不就是我們這個(gè)問題么!
an instance hang may result due to PMON getting
blocked when attempting to clean up a failed process.
從現(xiàn)象到問題本質(zhì)完全吻合!版本 10.2.0.4 也完全吻合!
當(dāng) PMON 要以 X 模式即獨(dú)占模式申請(qǐng) MUTEX(cursor:pin X 就是一種 mutex)去清理一個(gè)死去進(jìn)程的時(shí)候,該 MUTEX 被死去進(jìn)程持有!從而導(dǎo)致了數(shù)據(jù)庫 HANG 的情況!
問題原因與經(jīng)驗(yàn)總結(jié)
故障過程總結(jié):
1) SID=534 的進(jìn)程在持有 latch:library cache 和 mutex 等資源的時(shí)候進(jìn)程死去
2) PMON 有義務(wù)清理該進(jìn)程所持有的資源,如 mutex
3) 由于命中 BUG 5377099 ,導(dǎo)致 PMON 無法獲得 MUTEX,被死去的進(jìn)程 534 阻塞
4) 因此 SID=534 的死去進(jìn)程長時(shí)間持有 latch:library cache, 導(dǎo)致其他用戶執(zhí)行遞歸
SQL,無法被軟解析,繼而無法登陸,即數(shù)據(jù)庫出現(xiàn)了 HANG 的故障!
經(jīng)驗(yàn)總結(jié):
1) 運(yùn)維公式 = 快速收集系統(tǒng)即時(shí)狀態(tài)信息 + 恢復(fù)業(yè)務(wù)
2) 快速收集系統(tǒng)即時(shí)狀態(tài)信息的目的是做 RCA,根因分析,以便在大規(guī)模數(shù)據(jù)庫運(yùn)維中可以預(yù)防其他數(shù)據(jù)庫也出現(xiàn)類似問題。
3) 不定期做補(bǔ)丁分析,發(fā)現(xiàn)嚴(yán)重的 BUG,提前預(yù)防。
4)技巧重要,原理更重要。
通過這樣一個(gè)案例,你不難發(fā)現(xiàn),ORACLE 的 SSD 功能,真是強(qiáng)大的令人發(fā)指!