只用一个活动会话随机获得ORA-08177

时间:2010-02-24 14:40:34

标签: oracle odp.net ora-08177

我正在运行一个创建表格然后插入一些数据的程序 这是访问数据库的唯一程序 我随机收到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.NET 2.111.6.20 将隔离级别更改为ReadCommited可以解决问题,但我确实希望在Serializable级别运行此问题。
看起来像I'm not alone 有了这个问题,但没有给出答案,所以我再问一遍 我做错了什么,我怎么能解决这个问题?

由APC编辑

为了防止其他人咆哮错误的树,发布的代码示例只是ORA-8177错误的生成器。显然,实际代码是不同的;特别是,桌子的丢弃和重建是一个红色的鲱鱼。

2 个答案:

答案 0 :(得分:6)

完全重写(第一次咆哮错误的树)。

SERIALIZABLE隔离级别会在感兴趣的交易清单中抓取一个位置。如果Oracle无法获得一个插槽,那么它就会抛出ORA-8177。可用的ITL插槽数量由INITRANS和MAXTRANS控制。根据{{​​3}}:

  

使用可序列化模式,INITRANS   必须设置为至少3。

必须为表及其索引设置此项。那么,你的INITRANS设置是什么?当然,您的示例代码使用默认值(表为1,索引为2)。

答案 1 :(得分:5)

在评论中,用户Gary发布了一个解释这种奇怪行为的线程链接。 不久,有时在索引重构期间,撤消数据变得不可用。 任何以可序列化隔离级别运行并请求与此索引有某种关联的数据的事务都将获得ORA-08177。这是Oracle的半错误功能。 ROWDEPENDENCIES减少了出现此错误的机会。 对于我的应用程序,我只是切换到ReadCommited级别进行大数据上传。 似乎没有其他方法可以完全逃避这个问题。

谢谢,加里,我对其他问题提出了答案。