长查询可防止插入

时间:2009-02-13 21:05:45

标签: sql-server sql-server-2005

我有一个查询,每晚都会在一个包含大量记录(200,000+)的表上运行。这个应用程序简单地遍历结果(如果相关,则使用C#应用程序中的DbDataReader)并处理每个结果。处理完全在数据库之外完成。在应用程序迭代结果期间,我无法将任何记录插入到我要查询的表中。 insert语句只是挂起并最终超时。刀片在完全独立的应用中完成。

SQL Server是否在查询完成时锁定表格?这似乎是一种过于激进的锁定政策。我可以理解查询和新插入的记录之间可能存在冲突,但如果在查询开始后插入的记录根本没有包含在结果中,我会完全没问题。

有什么方法可以避免这种情况吗?

更新:
WITH(NOLOCK)绝对有效。正如你们中的一些人指出的那样,这不是最干净的方法。考虑到记录的数量,我不能真正地将所有内容查询到内存中,并且此表中的一些列是二进制的(某些记录实际上是大约1MB的总数据)。

另一个建议是,一次查询批量记录。这也不是一个坏主意,但它确实带来了一个新问题:独立于数据库的查询。现在,该应用程序可以与各种不同的数据库(Oracle,MySQL,Access等)一起使用。每个数据库都有自己的方式来限制查询中返回的行。但也许这可以更好地保存另一个问题?

回到主题,“WITH(NOLOCK)”子句肯定是SQL Server特有的,有没有办法阻止我的查询(从而阻止它与其他数据库一起工作)?也许我可以以某种方式在DbCommand对象上指定一个参数?或者我可以在数据库级别指定锁定策略吗?也就是说,更改SQL Server本身的某些属性会阻止表默认锁定吗?

8 个答案:

答案 0 :(得分:1)

这取决于您使用的隔离级别。您可以尝试使用With(NoLock)提示进行选择,这将阻止读取锁定,但也意味着正在读取的数据可能会在选择事务完成之前发生更改。

答案 1 :(得分:1)

您可以做的第一件事是尝试将“WITH(NOLOCK)”添加到查询中的任何表中。这将“驯服”SQL Server所做的锁定。在连接上使用“NOLOCK”的示例如下......

SELECT COUNT(Users.UserID)
    FROM Users WITH (NOLOCK)
       JOIN UsersInUserGroups WITH (NOLOCK) ON 
          Users.UserID = UsersInUserGroups.UserID

另一种选择是使用数据集而不是数据头。数据加载器是一种“消防软管”技术,在程序处理过程中保持与表格连接,并基本上通过软管逐行处理表格。数据集使用“断开连接”的方法,将所有数据加载到内存中,然后关闭连接。然后,您的程序可以将数据循环到内存中,而不必担心锁定。但是,如果这是一个非常大量的数据,可能存在内存问题。

希望这会有所帮助。

答案 2 :(得分:1)

如果您正在使用SQL Server 2005+,那么试试新的MVCC快照隔离怎么样。我用它得到了很好的结果:

ALTER DATABASE  SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE  SET READ_COMMITTED_SNAPSHOT ON;
ALTER DATABASE  SET MULTI_USER;

它将阻止读者阻止作者,反之亦然。它以极低的成本消除了许多死锁。

答案 3 :(得分:0)

如果在FROM子句中的表名后添加WITH(NOLOCK)提示,则应确保它不会锁定,并且它不关心读取被锁定的数据。如果你同时写作,你可能会得到“过时”的结果,但如果你不关心那么你应该没事。

答案 4 :(得分:0)

我认为避免这种情况的最佳方法是在SQL中而不是在应用程序中执行此操作。

您可以添加

WAITFOR DELAY'000:00:01'

在每次循环迭代结束时为其他进程提供运行时间 - 只需确保您没有启动TRANSACTION,以便所有其他进程都被锁定

答案 5 :(得分:0)

查询正在执行表锁定,因此插入失败。

听起来,就像你在处理结果时锁定桌面一样。 您应该将它们加载到某种数组或集合中,然后关闭数据库连接。 然后处理数组。

此外,在您选择使用时: WITH(NOLOCK)或WITH(READPAST)

答案 6 :(得分:0)

我不是使用锁定提示的忠实粉丝,因为你最终可能会出现脏读或其他奇怪现象。其他一些想法:

  • 你可以打破行数,这样你一次不能抓住200k吗?有没有办法判断你是否处理了一行 - 一个标志,一个时间戳 - 你可以用它来进行查询?您的查询可能是'SELECT TOP 5000 ...'每次获得5k差异。较短的查询意味着寿命较短的锁。

  • 如果您可以使用较小的行集,我喜欢DataSet与IDataReader的想法。您将把数据加载到内存中而不消耗任何SQL锁,但是内存量可能会导致其他问题。

-Brian

答案 7 :(得分:0)

您应该能够在.NET级别设置隔离级别,这样就不必包含WITH(NOLOCK)提示。

如果您想使用批处理选项,您应该能够从.NET级别指定Rowcount设置,这将告诉数据库仅返回 n 个记录数。通过在.NET级别设置这些设置,它们应该独立于数据库并在所有平台上运行。