2012-08-06 52 views
7

最近我遇到了一個奇怪的問題,在Oracle數據庫中編程:在可序列化的事務中,我做了一個批量插入(INSERT ... SELECT),之後立即在改變的表格上用SELECT鍵打開遊標。我認爲這個遊標會包含新插入的行,但令我驚訝的是,它的內容不穩定,有時包括所有新插入的行,有時只包含一個子集。Oracle:在可序列化事務中插入後立即選擇

我已經通過在打開遊標之前提交來解決了這個問題,但行爲讓我感到困惑。在同一個事務中插入一個insert後,如果沒有間接提交,實際上可以被信任嗎?或者,這種行爲是否與可序列化的事務相關?我試圖創建一個可重複的測試用例時,只有在添加索引(本例中爲主鍵索引,在實際代碼中爲普通索引)時才能夠獲得此行爲。也許問題在於構建索引所花費的時間,以便SELECT實際上使用不完整的索引來檢索結果?無論如何,這裏有一個可重複的測試用例:

-- Create empty source table 
CREATE TABLE TEST_CASE_1 AS 
    (SELECT 'CONTENT' AS CONTENT 
    FROM DUAL 
    WHERE 1 = 2) 

-- Add primary key 
ALTER TABLE TEST_CASE_1 
ADD CONSTRAINT TEST_CASE_1_PK PRIMARY KEY (CONTENT); 

-- Create empty destination table 
CREATE TABLE TEST_CASE_2 AS 
    (SELECT 'CONTENT' AS CONTENT 
    FROM DUAL 
    WHERE 1 = 2) 

-- Example of faulty code 
BEGIN 

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 

    -- Populate with 100.000 rows (I used ALL_OBJECTS but any source of 100.000 rows is good) 
    INSERT INTO TEST_CASE_1 
    (SELECT ROWNUM 
    FROM ALL_OBJECTS 
    WHERE ROWNUM <= 100000); 

    INSERT INTO TEST_CASE_2 
    (SELECT * 
    FROM TEST_CASE_1 
    WHERE CONTENT > 0); 

    COMMIT; 

END; 

在這個例子中,我希望TEST_CASE_2也有100.000行。重新生成這個測試用例(在一個無負載的數據庫中),我獲得了約400-500行插入。除去將事務設置爲可序列化的語句,我獲得了正確的100.000行計數。

+0

您是否在插入操作時在相同的數據庫會話中打開遊標?你在哪裏做這項工作 - 實際上是在數據庫還是從客戶端應用程序 - 如果後者你有連接池,並且你(有時)爲這兩個操作獲得不同的連接? – 2012-08-06 11:17:16

+0

遊標正在同一會話中打開。最初遊標在數據庫中打開,然後在.NET可執行文件中迭代,但爲了隔離問題,我製作了一個在數據庫中迭代遊標的過程版本,問題依然存在 - 事實上,在執行此操作之前,不要說服自己,問題出現在插入後的選擇中。 – user1578874 2012-08-06 11:37:41

+0

這不應該發生;來自[文檔](http://docs.oracle.com/cd/E11882_01/server.112/e25789/consist.htm#sthref1189)'在序列化隔離級別,事務僅查看在事務時提交的更改 - 不是查詢 - 由交易本身開始並進行更改「,因此您應該看到自己的更改。我猜可能是你的特定版本中的一個錯誤。承諾使得改變隔離級別有點毫無意義。你能發佈一個可重複的測試用例嗎? – 2012-08-06 17:20:04

回答

8

這似乎是一個錯誤;如果您有權訪問Oracle的支持網站,請參閱註釋1455175.1,其歷史可以追溯到8i。列出了一些錯誤編號(7592038-'隱藏數據來自選擇/更新新插入的行在SERIALIZABLE',6363019),但它們被關閉爲440317的重複項('隔離級別可序列化的原因未找到所選行的數據後插入「),儘管它最初是針對版本7(!)提出的,但仍顯示爲仍然開放並正在通過開發進行調查。

你似乎是對的,這是它與PK有關。列出的解決方法是:

  • 將執行的工作提交到該點。
  • 執行額外(但不同)的語句(可能在回滾到事務中早期建立的保存點之後)。
  • 回滾整個事務並從頭開始重新啓動事務。
  • 執行全表掃描並避免使用索引。

您知道第一個解決方法已經生效了,而且我不認爲第二個或第三個解決方案可以幫助您嗎?您可以嘗試第四個,爲第二個插入的選擇添加一個/*+ FULL(TEST_CASE_1) */提示。

我沒有在11.2.0.2(Linux)中看到錯誤,雖然我找不到任何暗示bug已經修復的錯誤;並且我沒有11.1環境來嘗試它 - 所以我無法檢查最後的解決方法是否適用於此測試用例。

有一個說明,你可以在11G中獲得ORA-08177。如果我在創建表之後過早運行匿名塊,或者插入的行太多,這似乎也與PK有關,那麼我遇到了這個問題。 This previous question可能是相關的。看起來像這樣會繼續是一個問題,所以如果變通方法無法幫助您,如果您確實需要更改隔離級別,則可能需要重新考慮;如果你這樣做,你可能不得不向Oracle提出服務請求以獲得更好的答案。

+0

非常感謝您的詳盡解答!我遇到的錯誤似乎與Oracle支持中的問題相對應。我很高興這是一個bug,因爲我們依賴於序列化事務處理系統中非常小但任務關鍵的部分,如果這些是可序列化事務的標準語義,我們可能不得不重寫整個部分。第一個解決方法(我使用的)在我的特定情況下很好,但在該程序的其他部分中沒有問題。我將在我的工具箱中保留所有的解決方法。 – user1578874 2012-08-07 14:02:25

+1

看來,這個bug *仍然*是不固定的;我剛剛在Windows上使用Oracle 12.1.0.1.0進行了複製。 – 2014-12-03 22:25:35

+1

剛剛在Oracle 12.1.0.2.0 - 64位Linux上獲得... – Jakob 2015-01-13 15:39:26

2

這是一個確認的錯誤,Oracle表示他們不打算修復它。下面是從他們對我的服務請求響應(2015年1月)的摘錄:

這些症狀是由於序列化交易被發現與 的已知問題和你的結論與錯誤440317是正確的。

錯誤440317 - ISOLATION LEVEL SERIALIZABLE原因FOUND上選擇AFTER INSERT
錯誤16803610個不進行任何數據 - INSERTED使用INSERT INTO ROWS迷失在串行化隔離級別TRANSAC

這兩種錯誤是發佈,所以你可以看到在MOS 錯誤搜索中的細節。

根據開發,與 有很長的歷史相同的問題有多個錯誤。設計不容易改變,因此沒有修復這個功能,直到找到這個功能並不是非常有用。

開發已經關閉了錯誤說代碼修復不可行。

提出的解決方法是
應用程序代碼modifiction:
變化的邏輯面前選擇
提交或不使用序列化
如果沒有應用程序代碼的修改:
不要使用主鍵或索引在桌面上

相關問題