Oracle臨時表游標(biāo)未釋放導(dǎo)致回滾段空間不足的解決方案
Oracle臨時表游標(biāo)未釋放導(dǎo)致回滾段空間不足時會報出多個ORA-01650錯誤,造成數(shù)據(jù)庫無法運行。本文我們主要就介紹了這一問題的解決方案,接下來就讓我們來一起了解一下這部分內(nèi)容。
先分析一下相關(guān)背景。產(chǎn)生報錯的程序是一個數(shù)據(jù)處理模塊,每天會將其他系統(tǒng)傳過來的平面文件中的內(nèi)容處理后放入數(shù)據(jù)庫中,事務(wù)量很大。我們的系統(tǒng)是9i,劃分了16個回滾段,其中兩個大的batch回滾段,每個batch回滾段有6G的足夠空間。而數(shù)據(jù)處理模塊會在事務(wù)中指定使用BATCH1。
再分析報錯的模塊。檢查相關(guān)的數(shù)據(jù)事務(wù)處理部分,由于業(yè)務(wù)需要保持?jǐn)?shù)據(jù)的一致性,需要處理完1個文件后才能提交,中間如果出錯就要全部回滾。經(jīng)過確認(rèn),這部分代碼有很長時間沒有做改動了。然后再確認(rèn)數(shù)據(jù)量,可以確認(rèn),今天(周五)是一周之內(nèi)文件內(nèi)容最少的一天。也就是說,如果由于數(shù)據(jù)量引起錯誤,其他時間的概率應(yīng)該更大。
但是,有一點需要注意。指定回滾段是針對事務(wù)的,不是針對回滾段。也就是說,我們可以指定某個事務(wù)只使用某個回滾段,但是不能保證這個回滾段只被這個事務(wù)事務(wù)。當(dāng)一個事務(wù)申請使用回滾段時,如果沒有自己指定,oracle就會根據(jù)當(dāng)時的各個回滾段的使用情況,分配一個最合適的回滾段給這個事務(wù)使用。因此,報回滾段空間不足的事務(wù)可能不一定就是導(dǎo)致回滾段空間不足的事務(wù)。還有一種可能就是,事務(wù)所指定的回滾段被其他事務(wù)所占用了。
于是我們就檢查是否還有其他事務(wù)占用了該回滾段。
- SELECT s.sid, s.username, s.osuser, s.machine, s.program,
- t.xidusn,t.ubafil,t.ubablk,t.used_ublk, t.log_io, t.phy_io, t.cr_get, t.cr_change,
- r.name, q.sql_text
- FROM v$session s,v$transaction t, v$RollName r, v$sqlarea q
- WHERE s.saddr=t.ses_addr
- and t.xidusn = r.usn
- and s.sql_address = q.address(+)
- and s.sql_hash_value = q.hash_value(+)And r,name = 'RBS_BATCH1';
果然發(fā)現(xiàn)有5個事務(wù)在占用BATCH1。但是發(fā)現(xiàn)會話狀態(tài)為INACTIVE。這說明它們當(dāng)時并沒有運行INSERT/UPDATE/DELETE語句(曾經(jīng)運行過,事務(wù)沒有結(jié)束),而是將回滾段資源hung住了。
再查下那些對象被hung在BATCH1中,
- select l.session_id, l.os_user_name, l.oracle_username, o.owner, o.object_name, t.xidusn,t.ubafil,t.ubablk,t.used_ublk, t.used_urec,
- t.log_io, t.phy_io, t.cr_get, t.cr_change, r.name
- from v$locked_object l, dba_objects o, v$transaction t, v$RollName r
- where l.object_id = o.object_id
- and l.xidusn = t.xidusn
- and l.xidslot = t.xidslot
- and l.xidsqn = t.xidsqn
- and t.xidusn = r.usn;
嗯,都是同一個對象:“TMP_CNT_GRP”。通過SID確認(rèn),確實上面的事務(wù)都是將這個對象hung在RBS中的。
經(jīng)過檢查,原來這個對象是一個臨時表。我們知道,臨時表對象平時是不存在數(shù)據(jù)的。只有當(dāng)一個會話使用臨時表,并向表中插入數(shù)據(jù)后,oracle才會在臨時表空間上創(chuàng)建它的數(shù)據(jù)對象。臨時表數(shù)據(jù)之所以只被所調(diào)用會話看到,是實際上是在每個會話中創(chuàng)建了一個單獨的數(shù)據(jù)對象,有各自的數(shù)據(jù)對象標(biāo)號。因此盡管是同一個臨時表,每個會話只是copy一個表結(jié)構(gòu),而創(chuàng)建了不同的數(shù)據(jù)對象,這樣,會話之間就不會有數(shù)據(jù)干擾。而在一個會話中,對臨時表數(shù)據(jù)對象的處理跟普通數(shù)據(jù)對象處理基本相同,其中就包括臨時表對象在事務(wù)中的數(shù)據(jù)改動也會有回滾信息的產(chǎn)生。
回到我們的問題中。通過V$SESSION和V$SQL_AREA查到,這些會話都是調(diào)用了一個PLSQL函數(shù),而且都是通過java調(diào)用的。
Review代碼,終于發(fā)現(xiàn)潛在問題了:這個函數(shù)的結(jié)果是返回一個游標(biāo),而游標(biāo)恰恰關(guān)聯(lián)了這張臨時表。
- INSERT INTO TMP_CNT_GRP ...
- SELECT...... ... open v_cursor FOR
- select TMP.CDE,
- CAR.ID,
- CAR.NME,
- COUNT(DISTINCT TMP.NUM) TOTAL_CNT
- from TMP_CNT_GRP TMP,
- CSS_CAR CAR
- WHERE TMP.ID = CAR.ID
- GROUP BY TMP.CDE, CAR.ID, CAR.NME;
- RETURN v_cursor;
- DELETE TMP_CNT_GRP;
(這段代碼其實還存在一個問題,也就是***的DELETE語句根本不會被調(diào)用)
從這段代碼中可以看到,實際上在整個函數(shù)當(dāng)中,臨時表的數(shù)據(jù)根本不會被釋放;而且也沒有提交和回滾事務(wù)(盡管這是一個會話級的臨時表)。占用的回滾段也不會被釋放。這就存在這樣的潛在問題,如果調(diào)用者不關(guān)閉會話或提交/回滾事務(wù)的話,它所占用的回滾段就不會被釋放。事實上,經(jīng)過讓java開發(fā)人員檢查代碼,果然發(fā)現(xiàn)客戶端在打開會話后,就沒有關(guān)閉,知道客戶端本身結(jié)束。
解決辦法:
1、因為這是一個會話級的臨時表,數(shù)據(jù)在事務(wù)提交后繼續(xù)保留,因此在PLSQL函數(shù)中的insert語句后加上commit;
2、Java代碼在使用完游標(biāo)后關(guān)閉會話。
關(guān)于Oracle數(shù)據(jù)庫的臨時表游標(biāo)未釋放導(dǎo)致回滾段空間不足的問題就介紹到這里了,希望本次的介紹能夠?qū)δ兴鶐椭?/p>
【編輯推薦】


2010-04-16 17:31:22
2016-12-27 15:47:19




