我们正在使用带有大型 select 语句的 SQL Server 2005 数据库(无行版本控制),并且我们看到它阻止其他语句运行(使用 看到sp_who2
)。我没有意识到 SELECT 语句可能会导致阻塞 - 我能做些什么来缓解这种情况吗?
6 回答
SELECT 可以阻止更新。正确设计的数据模型和查询只会导致最小的阻塞并且不会成为问题。'通常' WITH NOLOCK 提示几乎总是错误的答案。正确的答案是调整您的查询,使其不会扫描巨大的表。
如果查询不可调,那么您应该首先考虑SNAPSHOT ISOLATION level,其次您应该考虑使用DATABASE SNAPSHOTS并且最后一个选项应该是 DIRTY READS (并且最好更改隔离级别而不是使用 NOLOCK HINT)。请注意,脏读,顾名思义,将返回不一致的数据(例如,您的总工作表可能不平衡)。
从文档:
Shared (S)
锁允许并发事务(SELECT)
在悲观并发控制下读取资源。有关详细信息,请参阅Types of Concurrency Control
。shared (S)
当资源上存在锁时,没有其他事务可以修改数据。Shared (S)
读取操作完成后立即释放资源上的锁,除非事务隔离级别设置为可重复读取或更高,或者使用锁定提示shared (S)
在事务期间保留锁。
Ashared lock
与另一个共享锁或更新锁兼容,但与排他锁不兼容。
这意味着您的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)