导致阻塞的SQL Server SELECT语句
-
06-07-2019 - |
题
我们正在使用带有巨大select语句的SQL Server 2005数据库(没有行版本控制),我们看到它阻止其他语句运行(使用 sp_who2
看到)。我没有意识到SELECT语句可能导致阻塞 - 我有什么办法可以缓解这个问题吗?
解决方案
SELECT可以阻止更新。正确设计的数据模型和查询只会导致最小的阻塞而不是问题。 “通常”WITH NOLOCK提示几乎总是错误的答案。正确的答案是调整查询,使其不扫描大表。
如果查询无法解决,那么您应首先考虑 SNAPSHOT ISOLATION等级,第二,你应该考虑使用 DATABASE SNAPSHOTS ,最后一个选项应该是DIRTY READS(最好更改隔离级别而不是使用NOLOCK HINT)。请注意,如名称所示,脏读将返回不一致的数据(例如,您的总表单可能不平衡)。
其他提示
来自 文档 :
共享(S)
锁允许并发事务在悲观并发控制下读取(SELECT)
资源。有关更多信息,请参阅并发控制类型
。当资源上存在shared(S)
锁时,没有其他事务可以修改数据。一旦读取操作完成,就会释放资源上的共享(S)
锁,除非将事务隔离级别设置为可重复读取或更高,或者使用锁定提示来保留在事务持续期间共享(S)
锁。
共享锁
与另一个共享锁或更新锁兼容,但不与exlusive锁兼容。
这意味着您的 SELECT
查询将阻止 UPDATE
和 INSERT
查询,反之亦然。
SELECT
查询在从表中读取值块时会放置一个临时共享锁,并在读取完后将其删除。
对于存在锁定的时间,您将无法对锁定区域中的数据执行任何操作。
两个 SELECT
查询永远不会相互阻塞(除非它们是 SELECT FOR UPDATE
)
您可以在数据库上启用 SNAPSHOT
隔离级别并使用它,但请注意,它不会阻止 UPDATE
查询被 SELECT
查询(这似乎是你的情况)。
但它会阻止 SELECT
查询被 UPDATE
锁定。
另请注意, SQL Server
与 Oracle
不同,它使用锁管理器并将其锁定在内存链表中。
这意味着在负载很重的情况下,放置和删除锁定这一事实可能会很慢,因为链接列表本身应该被事务线程锁定。
要执行脏读,您可以:
using (new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions {
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
//Your code here
}
或
SelectCommand = "SELECT * FROM Table1 WITH (NOLOCK) INNER JOIN Table2 WITH (NOLOCK) ..."
请记住,您必须在每个要脏读的表
之后编写WITH(NOLOCK)您可以设置交易级别阅读未提交的
你可能也会陷入僵局:
和/或结果不正确:
“在READ COMMITTED下选择,REPEATABLE READ可能会返回错误的结果。”
您可以使用 WITH(READPAST)
表提示。它与 WITH(NOLOCK)
不同。它将在事务开始之前获取数据,不会阻止任何人。想象一下,您在事务开始之前运行了该语句。
SELECT * FROM table1 WITH (READPAST)