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.NET 2.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 回答