【问题标题】:Access DB Transaction Insert limit访问数据库事务插入限制
【发布时间】:2013-11-12 14:30:41
【问题描述】:

在您需要提交之前或在 Access/Jet 引发错误之前,您可以在 Access 事务中执行的插入数量是否有限制?

我目前正在运行以下代码,希望确定这个最大值是多少。

OleDbConnection cn =
                new OleDbConnection(
                    @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\temp\myAccessFile.accdb;Persist Security Info=False;");
            try
            {
                cn.Open();
                oleCommand = new OleDbCommand("BEGIN TRANSACTION", cn);               
                oleCommand.ExecuteNonQuery();    
                oleCommand.CommandText =
                            "insert into [table1] (name) values ('1000000000001000000000000010000000000000')";

                for (i = 0; i < 25000000; i++)
                {
                    oleCommand.ExecuteNonQuery();
                }                

                oleCommand.CommandText = "COMMIT";
                oleCommand.ExecuteNonQuery();
            }
            catch (Exception ex)
            {

            }
            finally
            {
                try
                {

                    oleCommand.CommandText = "COMMIT";
                    oleCommand.ExecuteNonQuery();                    
                }

                catch{}

                if (cn.State != ConnectionState.Closed)
                {
                    cn.Close();
                }
            }

当我在单个未提交事务中达到 2,333,920 次插入时,我在生产应用程序上收到的错误是:“超出文件共享锁计数。增加 MaxLocksPerFile 注册表项”。禁用事务解决了这个问题。

【问题讨论】:

  • 这个问题中没有足够的信息。有什么错误?真正的代码在哪里?你是如何连接到 Jet 的?
  • 添加了更多信息。 Tnx。

标签: c# transactions limit ms-access-2010


【解决方案1】:

我刚刚在 .accdb 数据库中的表上成功创建并提交了一个包含 5,000,000 次插入的事务。一个显着的区别是我没有直接发出BEGIN TRANSACTIONCOMMIT 语句,而是使用OleDbTransaction 对象:

cmd.CommandText =
    @"INSERT INTO Companies (ID, CompanyName) VALUES (?, ?)";
cmd.Parameters.Add("?", OleDbType.Integer);
cmd.Parameters.Add("?", OleDbType.VarWChar, 255);
cmd.Prepare();

OleDbTransaction tran =  con.BeginTransaction();
cmd.Transaction = tran;
for (int i = 1; i <= 5000000; i++)
{
    if ((i % 100) == 0)
    {
        Console.WriteLine(i.ToString());
    }
    cmd.Parameters[0].Value = i;
    cmd.Parameters[1].Value = "Company" + i.ToString();
    cmd.ExecuteNonQuery();
}
tran.Commit();

编辑回复:cmets

在成功创建并提交 10,000,000 次插入事务并验证 .Rollback() 确实工作(好建议,neoistheone!)后,我检查了事务的 IsolationLevel。 ACE.OLEDB 驱动程序的默认IsolationLevel 显然是ReadCommitted,根据MSDN 意味着:

在读取数据时会持有共享锁以避免脏读,但可以在事务结束之前更改数据,从而导致不可重复读取或幻像数据。

维基百科对那些不同的“阅读现象”here有很好的描述。

也许如果我要指定更严格的隔离级别(如Serializable),我可能会达到问题中描述的锁定限制,尽管由于这些都是 INSERT 操作,ACE.OLEDB 驱动程序可能只需要“隐藏”来自所有其他连接的插入,直到它们被提交。

编辑回复:性能

由于在事务中包装大量 INSERT 操作的一个相当常见的原因是为了让它们更快地完成,因此我测试了有和没有 OleDbTransaction 的一批 100,000 个插入。 (在这两种情况下都使用了准备好的语句。)如果没有OleDbTransaction,批处理大约需要 36 秒才能完成。使用默认隔离级别 (ReadCommitted) 启用 OleDbTransaction 将经过的时间减少到大约 23 秒。

【讨论】:

  • 这是一个有趣的区别。看来事务实际上可能是在本地管理的。
  • @neoistheone 显然OleDbTransaction 对象正在做某事来回避@user986363 遇到的限制。我刚刚尝试了一个 10,000,000 次插入的事务,它也有效。
  • @neoistheone 是的,.Rollback() 确实有效,至少对于 100,000 个插入的“小”测试。
  • 好的,那么显然它在事务中本地缓冲这些语句,然后在Commit 上执行它们。哇,我的意思是告诉你,坦率地说,我们在 IT 中必须解决的问题非常疯狂!
  • @neoistheone 它确实让事情变得有趣,这是肯定的。顺便说一句,我已经用更多细节更新了我的答案。
【解决方案2】:

是的,实际上是有限制的。你已经清楚地达到了它。每Microsoft's documentation

如果执行事务所需的锁数超过每个文件的最大锁数,则会发生错误。

在那个链接有几个解决方法。第二个更现实,它暂时改变了最大锁。

【讨论】:

  • 感谢文档链接。我会将插入分解为每个事务 500K 插入的较小批次,这应该可以为我们解决问题。
  • @user986363,没问题,很高兴能为您提供帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-29
  • 1970-01-01
  • 1970-01-01
  • 2011-01-14
  • 2014-08-26
  • 1970-01-01
  • 2010-10-23
相关资源
最近更新 更多