7

私は最近、Oracleデータベースでプログラミングするという奇妙な問題に遭遇しました.シリアライズ可能なトランザクション内で、一括挿入(INSERT ... SELECT)を実行し、その直後に、変更されたテーブルで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 行カウントが得られました。

4

2 に答える 2

8

これはバグのようです。Oracle のサポート Web サイトにアクセスできる場合は、ノート 1455175.1 を参照してください。これは 8i にさかのぼります。いくつかのバグ番号がリストされています (7592038 - 'SILENTLY INVISIBLE DATA FROM SELECT/UPDATE OF NEWLY INSERTED ROW IN SERIALIZABLE'、6363019) が、440317 の重複としてクローズされています ('ISOLATION LEVEL SERIALIZABLE CAUSES NO DATA FOUND ON ROWS SELECTED AFTER INSERT') は、まだオープンであり、開発によって調査されていることが示されています - 最初はバージョン 7(!) に対して提起されたにもかかわらずです。

それはPKに関連しているとあなたは正しいようです。リストされている回避策は次のとおりです。

  • その時点までに実行された作業をコミットします。
  • 追加の (ただし異なる) ステートメントを実行します (おそらく、トランザクションで以前に確立されたセーブポイントにロールバックした後)。
  • トランザクション全体をロールバックし、トランザクションを最初からやり直します。
  • 全表スキャンを実行し、索引の使用を避けてください。

最初の回避策はすでに効果的であり、2 番目または 3 番目の回避策は役に立たないと思いますか? /*+ FULL(TEST_CASE_1) */2 番目の挿入の選択にヒントを追加して、4 番目を試すことができます。

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

代わりに 11G で ORA-08177 を取得できるというメモがあります。テーブルを作成した直後に無名ブロックを実行した場合、またはあまりにも多くの行を挿入した場合に問題が発生しました。これも PK に関連しているようです。この前の質問が関連している可能性があります。

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

于 2012-08-07T12:40:09.160 に答える
2

これは確認済みのバグであり、Oracle は修正する予定はないと述べています。以下は、私のサービス リクエストに対する彼らの回答からの抜粋です (2015 年 1 月)。

これらの症状は、Serializable トランザクションが既知の問題で見つかったためであり、Bug 440317 の結論は正しいです。

バグ 440317 - ISOLATION LEVEL SERIALIZABLEにより、INSERT 後に選択された行にデータ
が見つからないバグ 16803610 - INSERT INTO を使用して挿入された行が、SERIALIZABLE ISOLATION LEVEL TRANSAC で失われる

これらのバグは両方とも公開されているため、MOS バグ検索で詳細を確認できます。

開発によると、非常に長い歴史を持つ同じ問題に対して複数のバグがありました。デザインを変更するのは簡単ではないため、この機能をフォークアウトする瞬間まで修正はありませんが、あまり役に立ちません。

開発は、コードの修正が不可能であると言ってバグを閉じました。

推奨される回避策は次の
とおりです。 アプリケーション コードの変更:ロジックを変更して、select または don't use serializable
の前にコミットする アプリケーション コードの変更なし: テーブルの主キーまたはインデックスを使用しない


于 2015-02-10T14:10:21.343 に答える