merge語句導致的ORA錯誤分析

jeanron100發表於2016-07-21
      最近處理了好幾起關於merge導致的問題,其實看到merge語句內心也還是蠻糾結的,這一次還是碰到了問題,簡直無語了。
      先交代下問題的背景。有一套OLTP環境和OLAP環境需要同步一部分資料,都是在每天的半夜開始,OLAP的庫的一個表資料會根據增量的邏輯從OLTP庫中同步,有兩種方式,一種是OLAP從OLTP中去抓取,另外一種是OLTP推送給OLAP。看起來表達的意思是差不多的,實現起來就是完全不同的風格,即一種主動一種被動,而對於大部分的應用需求來看,還是更傾向於OLAP從OLTP中去抓取這種方式,要不OLTP端還是存在一定的耦合度。
    

     但是目前的情況是OLTP主動推送給OLAP,當然這種設計方式看來達到的效果都是一致的,而且因為歷史原因,也是一直這麼用的。
在一次升級以後,藉著升級的機會,而且很巧又碰到了老版本相容merge語句的問題(又是merge),所以就自然而然想一併做個改進,把這個問題規範起來。
改進以後,OLAP會從OLTP中抓取資料,原來的邏輯完全不用修改,只是在兩個表關聯的地方,使用到了DB link來處理,所以程式碼層面可以認為是沒有其它的變化,而且在部署到OLAP端後,我建立了一個小表還測試了一把,沒有發現問題,所以簡單修改配置之後,就部署完成了,還給開發的同事講解了這個改進的意義,自己也還是蠻高興的。
    第二天同事就主動找我,說增量資料沒有同步過來,帶著程式設計師最常用的口吻“不可能啊”,“我這裡執行都好好的”之類的想法,我檢視了後臺的JOB執行日誌,結果發現日誌中竟然丟擲了一個ORA錯誤。
ORA-30926: unable to get a stable set of rows in the source tables
ORA-06512: at line 32
這個錯誤看起來似懂非懂,比較模糊,初步感覺是表結構哪裡出現了不一致的情況。
實現的大體思路如下:
insert into test.tmp_usercenter select *from xxx@OLAP where xxxx; --透過這種方式獲得增量資料
然後把增量資料插入test.test_user_center中。不匹配則插入
merge into test.test_user_center a
using (select * from tlbb.tmp_usercenter) b
on (a.uin=b.uin)
when matched then
update set LAST_LOGOUT=b.LAST_LOGOUT,
LAST_IP=b.LAST_IP,
FIRST_GAMESTYLE=b.FIRST_GAMESTYLE
when not matched then
insert (CN,
UIN,
BILL_TYPE,
PRESENT_POINT,。。。
OUTLINE_SCORE,FIRST_GAMESTYLE)
values
(
b.CN,
b.UIN,
b.BILL_TYPE,
b.PRESENT_POINT,。。。
b.FIRST_GAMESTYLE);
commit;
瞭解了大體的思路之後,我對裡面涉及的歷史表欄位都進行了認真的比對,沒有發現任何問題,增量的資料都進行了比對,都是和原來的生成方式一樣的。
但是為什麼這種方式就會出問題呢。這一點上我不好解釋,也不好給其他人解釋,所以我需要認真分析,看看到底是哪裡的問題。
同時我認真比對了ORA-30926錯誤的解釋,發現merge在處理一些DML的時候,如果同一記錄被影響變更多次,則會出現這類問題,那也就意味著表中的資料有冗餘的成分。
但是同樣的資料,同樣的邏輯,幾乎同樣的語句,怎麼原來可以,現在不可以呢。
最後反覆測試,發現原來還是DB link在這一點上有很大的差別,如果在正常情況下存在冗餘資料或者重複資料的情況下,使用merge來更新是會丟擲ORA-30926錯誤的。而使用DB Link竟然會把這個錯誤化解,而一直以來使用的在OLTP端推送資料至OLAP,在這個場景下使用了這樣的方式,看似也是碰到了一些問題,最後採用的那種方式,我不知道之前的人是否意識到那個問題,或者可能認為那種方式不支援這種需求,而我在嘗試改進的時候,就踩到了這個坑。當然我還是希望能夠在OLAP端完成這個需求,而原來可以,我們就可以借鑑成功的思想,所以改進起來就是耍個小聰明。DB link可以繞過這個ORA錯誤,那我們就使用DB link,但是訪問的是當前庫的表資料。即我們建立一個DB link指向自己,然後透過DB Link的方式訪問。這樣反覆測試,發現確實是可以的。
所以語句的邏輯就改為下面的形式:
insert into test.tmp_usercenter select *from xxx@OLAP where xxxx; --透過這種方式獲得增量資料
然後把增量資料插入test.test_user_center中。不匹配則插入
merge into test.test_user_center@OLAP_TEST a   --建立一個DB link OLTP_TEST指向當前的資料庫
using (select * from tlbb.tmp_usercenter) b
on (a.uin=b.uin)
when matched then
update set LAST_LOGOUT=b.LAST_LOGOUT,
LAST_IP=b.LAST_IP,
FIRST_GAMESTYLE=b.FIRST_GAMESTYLE
when not matched then
insert (CN,
UIN,
BILL_TYPE,
PRESENT_POINT,。。。
OUTLINE_SCORE,FIRST_GAMESTYLE)
values
(
b.CN,
b.UIN,
b.BILL_TYPE,
b.PRESENT_POINT,。。。
b.FIRST_GAMESTYLE);
commit;
這種方式能夠改進原來的ORA錯誤,權當做一種變相的改進吧。

#############################

每日發文,或技術、或總結,偶有日間小事也以為記,謂之學習筆記,成年累月800多天,中間幾乎沒有間斷,要旨只有一個:學習交流,共同進步 。

#############################

學習筆記精華整理,個人新書《Oracle DBA工作筆記》已開售,在京東,噹噹,亞馬遜,淘寶,天貓均有售,歡迎選購。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23718752/viewspace-2122373/,如需轉載,請註明出處,否則將追究法律責任。

相關文章