【问题标题】:Randomly getting ORA-08177 with only one active session只有一个活动会话随机获取 ORA-08177
【发布时间】:2010-02-24 14:40:34
【问题描述】:

我正在运行一个程序,该程序创建一个表,然后插入一些数据。
这是访问数据库的唯一程序。
我随机收到 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 错误的生成器。显然实际的代码是不同的;具体来说,删除和重新创建表格是一条红鲱鱼。

【问题讨论】:

    标签: oracle odp.net ora-08177


    【解决方案1】:

    完全重写(第一次发现错误的树)。

    SERIALIZABLE 隔离级别在感兴趣的事务列表中占据一个位置。如果 Oracle 无法获得一个插槽,那么它会抛出 ORA-8177。可用的 ITL 槽数由 INITRANS 和 MAXTRANS 控制。根据the documentation

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

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

    【讨论】:

    • 这只是一个演示这种奇怪行为的示例程序。真正的应用程序不会在每次启动时删除表。但是这个真实应用程序的测试套件会重新创建所有表,然后上传测试数据。那时我正面临这个问题。
    • @Denis:然后把表留在那里,并在代码中捕获由此产生的“表已存在”错误。
    • 这样的变通方法不是这里所期望的。有必要知道真正的原因。 // 我也知道这个问题
    • 不错的一个。在dbazine.com/oracle/or-articles/nanda3 有一篇关于 INITRANS 的文章,所以我猜插入工作直到块变得如此满以至于它无法分配新的 ITL 条目然后失败,因为事务不能等待自身。想知道为什么可序列化需要多个 ITL 条目。
    • 在这个问题的末尾有一些东西表明它与索引拆分有关,并且 ROWDEPENDENCIES 可能是一种解决方法。 asktom.oracle.com/pls/asktom/…
    【解决方案2】:

    在 cmets 用户 Gary 发布了一个线程链接,该链接解释了这种奇怪的行为。 不久,有时在索引重组期间撤消数据变得不可用。 任何以可序列化隔离级别运行并请求与该索引相关的数据的事务都将得到 ORA-08177。这是 Oracle 的一半错误一半功能。 ROWDEPENDENCIES 减少了出现此错误的机会。 对于我的应用程序,我只是切换到 ReadCommited 级别以进行大数据上传。 看来没有其他办法可以彻底摆脱这个问题了。

    谢谢,Gary,我已对您对其他问题的回答投了赞成票。

    【讨论】:

      猜你喜欢
      • 2016-08-20
      • 2013-11-24
      • 2010-11-03
      • 1970-01-01
      • 2021-11-06
      • 2019-08-26
      • 1970-01-01
      • 2013-11-10
      • 2018-01-17
      相关资源
      最近更新 更多