18

我们正在使用带有大型 select 语句的 SQL Server 2005 数据库(无行版本控制),并且我们看到它阻止其他语句运行(使用 看到sp_who2)。我没有意识到 SELECT 语句可能会导致阻塞 - 我能做些什么来缓解这种情况吗?

4

6 回答 6

34

SELECT 可以阻止更新。正确设计的数据模型和查询只会导致最小的阻塞并且不会成为问题。'通常' WITH NOLOCK 提示几乎总是错误的答案。正确的答案是调整您的查询,使其不会扫描巨大的表。

如果查询不可调,那么您应该首先考虑SNAPSHOT ISOLATION level,其次您应该考虑使用DATABASE SNAPSHOTS并且最后一个选项应该是 DIRTY READS (并且最好更改隔离级别而不是使用 NOLOCK HINT)。请注意,脏读,顾名思义,将返回不一致的数据(例如,您的总工作表可能不平衡)。

于 2009-06-19T09:58:14.603 回答
15

文档

Shared (S)锁允许并发事务(SELECT)在悲观并发控制下读取资源。有关详细信息,请参阅Types of Concurrency Controlshared (S)当资源上存在锁时,没有其他事务可以修改数据。Shared (S)读取操作完成后立即释放资源上的锁,除非事务隔离级别设置为可重复读取或更高,或者使用锁定提示shared (S)在事务期间保留锁。

Ashared lock与另一个共享锁或更新锁兼容,但与排他锁不兼容。

这意味着您的SELECT查询将阻塞UPDATEINSERT查询,反之亦然。

查询从SELECT表中读取数据块时会放置一个临时共享锁,并在完成读取时将其移除。

在锁定存在期间,您将无法对锁定区域中的数据进行任何操作。

两个SELECT查询永远不会互相阻塞(除非它们是SELECT FOR UPDATE

您可以在数据库上启用SNAPSHOT隔离级别并使用它,但请注意,它不会阻止UPDATE查询被SELECT查询锁定(这似乎是您的情况)。

但是,它将防止SELECT查询被UPDATE.

另请注意SQL Server,与 不同的是Oracle,它使用锁管理器并将其锁定在内存链表中。

这意味着在重负载下,仅仅放置和删除锁可能会很慢,因为链表本身应该被事务线程锁定。

于 2009-06-19T09:55:17.230 回答
2

要执行脏读,您可以:

 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)

于 2009-06-19T11:53:12.767 回答
0

您可以将事务级别设置为读取未提交

于 2009-06-19T09:58:10.843 回答
0

您可能还会遇到死锁:

“只涉及一张表的死锁” http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/01/reproduce-deadlocks-involving-only-one-table.aspx

和/或不正确的结果:

“在 READ COMMITTED 和 REPEATABLE READ 下选择可能会返回不正确的结果。”

http://www2.sqlblog.com/blogs/alexander_kuznetsov/archive/2009/04/10/selects-under-read-committed-and-repeatable-read-may-return-incorrect-results.aspx

于 2009-06-19T13:32:14.377 回答
0

您可以使用WITH(READPAST)表格提示。它不同于WITH(NOLOCK). 它将在事务开始之前获取数据,并且不会阻止任何人。想象一下,您在事务开始之前运行了语句。

SELECT * FROM table1  WITH (READPAST)
于 2019-02-06T06:31:08.940 回答