前言
成都創(chuàng)新互聯(lián)主要從事成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)莒南,十余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專(zhuān)業(yè),歡迎來(lái)電咨詢(xún)建站服務(wù):18980820575
編者注,本文的知識(shí)點(diǎn)較多,請(qǐng)補(bǔ)充足夠正能量后閱讀,看完會(huì)讓你質(zhì)疑你的DBA生涯,作者的水平代表著國(guó)內(nèi)Oracle TroubleShooting最高水平。
很早就想把ORA-1000的問(wèn)題,總結(jié)成一個(gè)話題。機(jī)緣巧合,最近恰好遇上幾個(gè)此類(lèi)問(wèn)題,特分享分析過(guò)程出來(lái)供大家參考。
首先,ORA-1000報(bào)錯(cuò)是什么意思呢?我們來(lái)看看官方的解釋?zhuān)?/p>
非常簡(jiǎn)單,單個(gè)進(jìn)程打開(kāi)的游標(biāo)數(shù)超過(guò)了最大值,也就是超過(guò)了數(shù)據(jù)庫(kù)設(shè)置的open_cursos參數(shù)的值;面對(duì)這樣的情況,不同的DBA會(huì)有不同的解決方案:調(diào)整open_cursors參數(shù)?還是直接把問(wèn)題拋給開(kāi)發(fā)人員?
今天老貓就來(lái)給大家分享一個(gè)處理ORA-1000的經(jīng)典案例,看看大家的處理方式和我們的處理方式有什么不一樣?在處理過(guò)程中老貓又用到了什么不一樣的處理技巧?處理完能收獲什么樣的心得體會(huì)?
2
來(lái)活啦
客戶(hù)來(lái)電求助,系統(tǒng)周期性地報(bào)ORA-1000的錯(cuò)誤,之前客戶(hù)已經(jīng)多次調(diào)整open_cursors參數(shù),目前已經(jīng)調(diào)整到了2000了,難道還是接著一直往上調(diào)?open_cursors是針對(duì)一個(gè)單個(gè)進(jìn)程打開(kāi)cursor數(shù)的限制,對(duì)于一般應(yīng)用來(lái)說(shuō),如果能及時(shí)關(guān)閉cursor,2000個(gè)已經(jīng)足夠使用,那么這里是客戶(hù)沒(méi)有正確關(guān)閉游標(biāo)還是其本身就需要同時(shí)打開(kāi)大量游標(biāo)抑或是其他原因呢,這已經(jīng)極大地勾起了我的興趣!找出根因,并提供解決方案,是我們?cè)诜?wù)客戶(hù)過(guò)程中的一貫態(tài)度。
3
捕捉信息
面對(duì)ORA-1000這種應(yīng)用級(jí)的錯(cuò)誤,我們一般可以通過(guò)設(shè)置errorstack時(shí)收集報(bào)錯(cuò)進(jìn)程的process dump來(lái)進(jìn)行分析;而如果報(bào)錯(cuò)沒(méi)那么容易出現(xiàn),我們就會(huì)選擇在之前拋錯(cuò)業(yè)務(wù)頻繁執(zhí)行的時(shí)段做個(gè)systemstate dump,主要目的是可以看看這類(lèi)業(yè)務(wù)執(zhí)行時(shí)server process中都o(jì)pen了哪些cursor,然后根據(jù)現(xiàn)象進(jìn)行進(jìn)一步的分析,判斷問(wèn)題的原因;
這里客戶(hù)系統(tǒng)是周期性的報(bào)ORA-1000錯(cuò)誤,于是建議客戶(hù)開(kāi)errorstack,收集trace文件進(jìn)行分析。
4
開(kāi)始分析
首先是設(shè)置1000的errorstack;
ORA-1000錯(cuò)誤發(fā)生時(shí),會(huì)生成一個(gè)trace,通過(guò)觀察trace發(fā)現(xiàn)在,確實(shí)打開(kāi)了2000個(gè)cursor,在trace中搜索cursor#可以看到,發(fā)現(xiàn)cursor都是打開(kāi)的同一個(gè)SQL:SELECT activityno,ruleno FROM T_RM_COUPONINF;
那么問(wèn)題來(lái)了,為什么對(duì)同一個(gè)SQL會(huì)打開(kāi)這么多cursor呢?仔細(xì)觀察一下我們就會(huì)發(fā)現(xiàn)這個(gè)SQL的特別之處,Excutioncount=0&LoadCount=299&InvalidationCount=300,還記得老貓分享的第十二期的故事么,這是典型的解析錯(cuò)誤的問(wèn)題!
不過(guò)這里我們就不用設(shè)置10035事件去確認(rèn)了,我們?cè)噲D來(lái)解析SQL:SELECT activityno,ruleno FROM T_RM_COUPONINF;很快我們就發(fā)現(xiàn)了問(wèn)題,T_RM_COUPONINF這個(gè)對(duì)象根本就不存在!
到這里,看起來(lái)我們發(fā)現(xiàn)了兩個(gè)問(wèn)題:
1. 進(jìn)程中打開(kāi)了一條錯(cuò)誤SQL的cursor;
2. 在遇到編譯錯(cuò)誤后,進(jìn)程沒(méi)有及時(shí)關(guān)閉cursor,似乎對(duì)這段代碼的處理過(guò)程沒(méi)有加catchexecption或者finally的過(guò)程;
那么是誰(shuí)發(fā)起了這條SQL,在沒(méi)有編譯成功的情況下,沒(méi)有關(guān)閉cursor呢?模擬這樣的代碼一點(diǎn)都不難,腦子里閃過(guò)無(wú)數(shù)個(gè)草泥馬…罵誰(shuí)還不好說(shuō),先冷靜,由于這個(gè)問(wèn)題可以在測(cè)試環(huán)境重現(xiàn)。
5
定位來(lái)源
通常來(lái)說(shuō),數(shù)據(jù)庫(kù)中運(yùn)行的SQL可以分為兩種,普通SQL和遞歸SQL;由于這里,sql本身存在錯(cuò)誤,所以我們無(wú)法直接定位SQL到底是來(lái)自外部(普通SQL)還是數(shù)據(jù)庫(kù)(遞歸SQL);這時(shí),我們就可以使用10046事件來(lái)幫忙了;
可以看到10046的trace中有如上一條解析錯(cuò)誤的記錄,err=942即拋錯(cuò)ORA-942,意味著表或視圖不存在;同時(shí)還有另一關(guān)鍵的點(diǎn):dep=0,即SQL的遞歸深度為0,也就是非遞歸SQL!所以,只能找開(kāi)發(fā)來(lái)確認(rèn)了,開(kāi)發(fā)給出明確的答復(fù)卻讓人沮喪:這條SQL不是應(yīng)用發(fā)出來(lái)的!
不是應(yīng)用寫(xiě)的,也不是數(shù)據(jù)庫(kù)自己產(chǎn)生的,那么這SQL來(lái)自于哪里呢?我再仔細(xì)捋了捋思路,發(fā)現(xiàn)我們?cè)谌粘L幚韱?wèn)題的過(guò)程中,經(jīng)常忽略的一個(gè)環(huán)節(jié),那就是JDBC包;一般認(rèn)為,JDBC主要是為了維護(hù)應(yīng)用與數(shù)據(jù)庫(kù)的連接的,但實(shí)際上,JDBC在這個(gè)過(guò)程中也是有可能執(zhí)行一些SQL的,甚至可以通過(guò)一些配置重新封裝應(yīng)用程序發(fā)到數(shù)據(jù)庫(kù)服務(wù)器的SQL語(yǔ)句,在這個(gè)過(guò)程中,出現(xiàn)一些問(wèn)題也是可能的。
至此,我們暫時(shí)將問(wèn)題定位到JDBC上;查看JDBC版本,是12.1.0.2數(shù)據(jù)庫(kù)自帶的。OK,范圍縮小到JDBC。下一步debug JDBC。
6
不一樣的trace
前面初步懷疑到了JDBC上,接下來(lái)需要做的就是通過(guò)在應(yīng)用代碼中打開(kāi)JDBC的trace即可,增加的代碼如下:
觀察結(jié)果JDBC trace文件:
沒(méi)錯(cuò),我們看到了那條SQL的身影,看來(lái)錯(cuò)誤SQL確實(shí)是來(lái)自于JDBC;
7
原因確認(rèn)
原來(lái),應(yīng)用持久化框架里為了取得SQL的綁定變量信息,調(diào)用Oracle JDBC的PreparedStatement.getParameterMetaData()函數(shù), 在這個(gè)方法里,JDBC取得parameter metadata的方法是:生成一條SQL:SELECT activityno, ruleno FROM T_RM_COUPONINFO,通過(guò)編譯這條SQL,取得activityno, ruleno兩個(gè)列的metadata信息,然后返回給客戶(hù)應(yīng)用。
不幸的是:在生成這條SQL時(shí),出現(xiàn)了錯(cuò)誤----丟掉了一個(gè)字母O,導(dǎo)致ORA-942的錯(cuò)誤。錯(cuò)就錯(cuò)了吧,你至少要關(guān)掉cursor呀。JDBC的開(kāi)發(fā)疏忽了。
正確sql:
SELECT activityno, ruleno FROM T_RM_COUPONINFO
目前解析成的sql:
SELECT activityno, ruleno FROM T_RM_COUPONINF
這是JDBC遞歸SQL引發(fā)的一個(gè)問(wèn)題,其實(shí)是JDBC12.1.0.2的一個(gè)BUG。
8
解決問(wèn)題
將收集的errorstack和JDBC trace提交到oracle support;GCS還是蠻給力的,不長(zhǎng)時(shí)間就提供了一個(gè)patch。
9
老貓小提示
大家可能會(huì)疑惑,為什么老貓開(kāi)的SR反饋這么快,我開(kāi)的SR卻遲遲沒(méi)有進(jìn)展呢?
其實(shí),老貓作為從原廠出來(lái)的老司機(jī),就這個(gè)CASE而言,你要了解下面兩點(diǎn):
1. JDBC在Oracle內(nèi)部是個(gè)很跨界的產(chǎn)品,它的support其實(shí)是負(fù)責(zé)中間件的工程師,而不是Oracle Database工程師。當(dāng)你開(kāi)一個(gè)ORA-1000的問(wèn)題給Database工程師時(shí),你實(shí)在不能指望他會(huì)java;當(dāng)你開(kāi)給中間件工程師,又不能指望他對(duì)Database有多深的了解,勢(shì)必扯皮。老貓?jiān)谠瓘S時(shí),遇上這類(lèi)問(wèn)題,通常是在五樓陽(yáng)臺(tái)抽煙時(shí),互相溝通一小會(huì)兒的。大家工作壓力都很大,還沒(méi)到自己手里的問(wèn)題,沒(méi)人真的上心。
2. 能重現(xiàn)的問(wèn)題,盡量作一個(gè)test case給Oracle support,把能提供的信息提供好。在這個(gè)案例中,我們把現(xiàn)象,問(wèn)題發(fā)生的函數(shù),trace證據(jù)全都提供給support,減少了交互,減少了扯皮。所以,問(wèn)題解決得很快。
總而言之,對(duì)于每個(gè)自己的SR,我們盡量提供自己所了解的所有信息,SR的進(jìn)展就會(huì)更順利。
10
問(wèn)題總結(jié)
這個(gè)問(wèn)題,我們看到,作為一個(gè)好的DBA,絕對(duì)不能只是改改參數(shù),或者是將看起來(lái)與數(shù)據(jù)庫(kù)無(wú)關(guān)的問(wèn)題一手推給開(kāi)發(fā)人員;我們可以做的是,把問(wèn)題定位到最小范圍內(nèi),至于確確實(shí)實(shí)涉及到數(shù)據(jù)庫(kù)內(nèi)層不公開(kāi)的部分,只要提供足夠的信息,我們相信SR也能幫助我們定位到數(shù)據(jù)庫(kù)底層的問(wèn)題。