22

テーブルを作成してからデータを挿入するプログラムを実行しています。
これは、データベースにアクセスする唯一のプログラムです。
ランダムにORA-08177を取得しています。
実際のコードはやや複雑ですが、この動作を再現する簡単なプログラムを作成しました。

using System;
using System.Data;
using Oracle.DataAccess.Client;

namespace orabug
{
  class Program
  {
    private const string ConnectionString = ""; // Valid connection string here

    // Recreates the table
    private static void Recreate()
    {
      using (var connection = new OracleConnection(ConnectionString)) {
        connection.Open();
        using (var command = connection.CreateCommand()) {
          command.CommandText = @"
            declare
              table_count binary_integer;
            begin
              select count(*) into table_count from sys.user_tables where table_name = 'TESTTABLE';
              if table_count > 0 then
                execute immediate 'drop table TestTable purge';
              end if;
              execute immediate 'create table TestTable(id nvarchar2(32) primary key)';
            end;";
          command.ExecuteNonQuery();
        }
        connection.Close();
      }
    }

    // Opens session sessionCount times, inserts insertCount rows in each session.
    private static void Insert(int sessionCount, int insertCount)
    {
      for (int sessionNumber = 0; sessionNumber < sessionCount; sessionNumber++)
        using (var connection = new OracleConnection(ConnectionString)) {
          connection.Open();
          using (var transaction = connection.BeginTransaction(IsolationLevel.Serializable)) {
            for (int insertNumber = 0; insertNumber < insertCount; insertNumber++)
              using (var command = connection.CreateCommand()) {
                command.BindByName = true;
                command.CommandText = "insert into TestTable (id) values(:id)";
                var id = Guid.NewGuid().ToString("N");
                var parameter = new OracleParameter("id", OracleDbType.NVarchar2) {Value = id};
                command.Parameters.Add(parameter);
                command.Transaction = transaction;
                command.ExecuteNonQuery();
              }
            transaction.Commit();
          }
          connection.Close();
        }
    }

    static void Main(string[] args)
    {
      int iteration = 0;
      while (true) {
        Console.WriteLine("Running iteration: {0}", iteration);
        try {
          Recreate();
          Insert(10, 100);
          Console.WriteLine("No error");
        }
        catch (Exception exception) {
          Console.WriteLine(exception.Message);
        }
        iteration++;
      }
    }
  }
}

このコードは無限サイクルで実行されます。
各反復で、次のアクションを10回実行します。

  • オープンセッション

  • ランダムデータで100行を挿入します

  • セッションを閉じる

  • エラーが発生しなかったことを示すメッセージを表示します

エラーが発生した場合、例外がキャッチされ、そのメッセージが出力されてから、次の反復が実行されます。

これがサンプル出力です。ご覧のとおり、ORA-08177はランダムにインターリーブを成功させています。

Running iteration: 1
No error
Running iteration: 2
ORA-08177: can't serialize access for this transaction
Running iteration: 3
ORA-08177: can't serialize access for this transaction
Running iteration: 4
ORA-08177: can't serialize access for this transaction
Running iteration: 5
ORA-08177: can't serialize access for this transaction
Running iteration: 6
ORA-08177: can't serialize access for this transaction
Running iteration: 7
No error
Running iteration: 8
No error
Running iteration: 9
ORA-08177: can't serialize access for this transaction
Running iteration: 10
ORA-08177: can't serialize access for this transaction
Running iteration: 11
ORA-08177: can't serialize access for this transaction
Running iteration: 12
ORA-08177: can't serialize access for this transaction
Running iteration: 13
ORA-08177: can't serialize access for this transaction
Running iteration: 14
ORA-08177: can't serialize access for this transaction
Running iteration: 15
ORA-08177: can't serialize access for this transaction
Running iteration: 16
ORA-08177: can't serialize access for this transaction
Running iteration: 17
No error
Running iteration: 18
No error
Running iteration: 19
ORA-08177: can't serialize access for this transaction
Running iteration: 20
No error

Oracle 11.1.0.6.0を実行していて、ODP.NET2.111.6.20を使用しています。
問題を修正するために分離レベルを変更しますが、私は本当にこれをレベルReadCommitedで実行したいと思います。この問題は私だけではない ようです が、答えが出なかったので、もう一度質問します。 私は何を間違っているのですか、そしてどうすればこれを修正できますか? Serializable

APCによる編集

他の誰かが間違ったツリーを吠えるのを防ぐために、投稿されたコードサンプルはORA-8177エラーの単なるジェネレータです。どうやら実際のコードは異なります。具体的には、テーブルの削除と再作成は赤いニシンです。

4

2 に答える 2

8

完全な書き直し(最初に間違ったツリーを吠えた)。

SERIALIZABLE分離レベルは、関心のあるトランザクションリストのスロットを取得します。Oracleがスロットを取得できない場合は、ORA-8177を投げます。使用可能なITLスロットの数は、INITRANSおよびMAXTRANSによって制御されます。ドキュメントによると:

シリアル化可能モードを使用するには、INITRANSを少なくとも3に設定する必要があります。

これは、テーブルとそのインデックスの両方に設定する必要があります。それで、あなたのINITRANS設定は何ですか?確かに、サンプルコードはデフォルト値(テーブル用に1、インデックス用に2)を使用しています。

于 2010-02-24T18:24:56.953 に答える
6

コメントで、ユーザーGaryは、この奇妙な動作を説明するスレッドへのリンクを投稿しました。間もなく、インデックスの再構築中に元に戻るデータが使用できなくなることがあります。シリアル化可能な分離レベルで実行され、このインデックスに何らかの形で関連するデータを要求するトランザクションは、ORA-08177を取得します。これは、Oracleのハーフバグのハーフ機能です。ROWDEPENDENCIESは、このエラーが発生する可能性を減らします。私のアプリケーションでは、大量のデータをアップロードするためにReadCommitedレベルに切り替えただけです。この問題を完全に回避する方法は他にないようです。

ありがとう、ゲイリー、私は他の質問へのあなたの答えに賛成しました。

于 2010-02-26T09:38:49.867 に答える