2012-08-06 16 views
7

最近、私は奇妙な問題に遭遇しました。つまり、シリアライズ可能なトランザクションの中で大量挿入(INSERT ... SELECT)を行い、直後変更されたテーブルでSELECTを使用してカーソルを開きます。このカーソルには新しく挿入された行が含まれていると思われましたが、驚いたことにその内容は不規則で、時には新しく挿入された行がすべて含まれていることがあります。Oracle:シリアライズ可能なトランザクションに挿入した直後に選択

私はカーソルを開く前にコミットすることでこの問題を解決しましたが、その動作は私を困惑させました。 intervining commitなしで、同じトランザクション内の挿入後にselectを実際に信頼できるか?または、この動作は何らかの形でトランザクションがシリアライズ可能であることに関係していますか?

フォローアップ再現可能なテストケースを作成しようとすると、インデックス(この場合はプライマリキーインデックス、実際のコードでは通常のインデックス)を追加した後でしかこの動作を取得できませんでした。おそらく問題は、索引の作成に費やされた時間にあります。その結果、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

挿入を実行しているのと同じデータベースセッションでカーソルを開いていますか?実際にデータベースやクライアントアプリケーションのどこでこの作業をしていますか?後者に接続プールがあり、2つのアクションの接続が異なる場合があります。 –

+0

カーソルが同じセッションで開かれています。もともとはカーソルがデータベースで開かれていて.NET実行ファイルで反復されていましたが、問題を特定するためにデータベース内のカーソルを反復したプロシージャーを作成しましたが、その問題がインサートの後のセレクトにあったことを自分自身に納得させる。 – user1578874

+0

これは起こりません。 [the docs](http://docs.oracle.com/cd/E11882_01/server.112/e25789/consist.htm#sthref1189)から 'シリアル化分離レベルでは、トランザクションはトランザクションの時点でコミットされた変更のみを認識します - クエリ自体が開始されず、トランザクション自体によって行われた変更ではないため、独自の変更が表示されるはずです。私が推測するあなたの特定のバージョンのバグかもしれません。コミットすると、アイソレーションレベルが少し無意味に変更されます。再現可能なテストケースを投稿できますか? –

答えて

8

これはバグのようです。オラクル社のサポートWebサイトにアクセスしている場合は、8iまでの注釈1455175.1を参照してください。いくつかのバグ番号が表示されています(7592038 - 'SERIALIZABLEの新しく挿入された行のSELECT/UPDATEからの黙って表示されないデータ'、6363019)が、440317の重複として閉じられています( 'ISOLATION LEVEL SERIALIZABLE原因は選択された行にデータがありませんAFTER INSERT ')、これはもともとはバージョン7(!)に対して作成されていましたが、まだ開かれており、開発によって調査されています。

あなたが正しいと思われるのは、PKに関連することです。回避策は次のとおりです。

  • その時点で実行された作業をコミットします。
  • 追加の(ただし異なる)文を実行します(おそらく、トランザクションの前に確立されたセーブポイントにロールバックした後)。
  • トランザクション全体をロールバックし、トランザクションを最初から再開します。
  • フル・テーブル・スキャンを実行し、インデックスを使用しないでください。

あなたは最初の回避策が既に有効であることを知っていますが、2番目または3番目の解決策は役に立たないと思いますか?第2のインサートの選択に/*+ FULL(TEST_CASE_1) */ヒントを追加して、第4のインサートを試すことができます。

11.2.0.2(Linux)でエラーは発生しませんが、バグが修正されたことを示すものは見つかりませんでした。試してみるには11.1の環境がないため、このテストケースに最後の回避策が適用されていることを確認することはできません。

代わりに11GでORA-08177を入手できるという注意があります。テーブルを作成したあとで無名ブロックを実行した場合や、あまりにも多くの行が挿入されていた場合は、PKに関連しているように見えます。 This previous questionが適切かもしれません。

このような問題は引き続き発生しますので、回避策が役立たない場合は、本当に分離レベルを変更する必要があるかどうか再考する必要があります。あなたがそうした場合は、より良い答えを得るためにOracleにサービス要求を提出する必要があるかもしれません。

+0

徹底的な回答ありがとうございました!私が経験しているバグは、Oracleサポートの問題に対応しているようです。私たちはシステムの非常に小さいがミッションクリティカルな部分に対してはシリアライズ可能なトランザクションに依存しているので、バグだとうれしいです。これらがシリアライズ可能なトランザクションの標準的なセマンティクスであれば、セクション全体を書き直す必要があります。最初の回避策(私が使用した)は、私の特定のシナリオでは問題ありませんが、プログラムの他の部分では問題ありません。私は回避策のすべての兵器を私のツールボックスに保管します。 – user1578874

+1

このバグはまだ修正されていないようです。 Windows上でOracle 12.1.0.1.0を使用して再現しました。 –

+1

ちょうどOracle 12.1.0.2.0 - 64bit Linuxでそれを手に入れました... – Jakob

2

これは確認済みのバグであり、Oracleは修正を予定していないと述べています。ここに私のサービスリクエスト(2015年1月)への応答からの抜粋です:あなたはこれらの症状が 既知の問題を持つ直列化取引に起因することが見出されている

とバグ440317との結論は正しいです。

バグ440317 - ISOLATIONのLEVEL SERIALIZABLE原因 バグ16803610 INSERT
後に、選択行のデータが見つかりません - あなたが見ることができるようにINSERT INTOを使用して挿入行は、これらのバグが公開されているSERIALIZABLE分離レベルTRANSAC

どちらにして失われましたMOS バグ検索の詳細。

開発当時、 の非常に長い履歴で、同じ問題で複数のバグがありました。デザインは変更するのが簡単ではないので、この機能を忘れるまでは の修正はありません。あまり役に立ちません。

開発では、コード修正が実行できないというバグをクローズしました。提案

回避策は
アプリケーションコードのmodifictionです:
変更ロジックを使用して選択
前にコミットまたはいけない持っているために、シリアライズ、アプリケーション・コードの変更なし

は、主キーまたはインデックスを使用しないでください。テーブル上で

関連する問題