死锁是什么引起的?
我们需要重提一下从-T1222输出中提取出来的死锁概述(参见SQL Server 死锁故障排除,第一部分,复习一下解码-T1222):
Spid X is running this query (line 2 of proc [p1], inputbuffer “… EXEC p1 4 …”):
SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
Spid Y is running this query (line 2 of proc [p2], inputbuffer “EXEC p2 4”):
UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
The SELECT is waiting for a Shared KEY lock on index t1.cidx.The UPDATE holds a conflicting X lock.
The UPDATE is waiting for an eXclusive KEY lock on index t1.idx1.The SELECT holds a conflicting S lock.
SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
|--Nested Loops(Inner Join, OUTER REFERENCES:([Uniq1002], [t1].[c1]))
|--Index Seek(OBJECT:([t1].[idx1]), SEEK:([t1].[c2] >= [@p1] AND [t1].[c2] <= [@p1]+(1)) ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([t1].[cidx]), SEEK:([t1].[c1]=[t1].[c1] AND [Uniq1002]=[Uniq1002]) LOOKUP ORDERED FORWARD)
一个内嵌的循环连接执行它的第一个子运算符一次,然后为每个由第一个子运算符返回的行执行第二个运算符(详见此博文)。在这种情况下,第一个子运算符是非聚集索引搜索,查找“WHERE c2 BETWEEN @p1 AND @p1+1”的行。在非聚集索引中,为每一个限定的行,在聚集索引上执行第二个搜索,以查找整个数据行。聚集索引搜索是必须的,因为非聚集索引不能替代这个查询。如果你正在运行SQL2000,你会看到一个不同的样子的计划:
SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
|--Bookmark Lookup(BOOKMARK:([Bmk1000]), OBJECT:([t1]))
|--Index Seek(OBJECT:([t1].[idx1]), SEEK:([t1].[c2] >= [@p1] AND [t1].[c2] <= [@p1]+1) ORDERED FORWARD)
UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
|--Clustered Index Update(OBJECT:([t1].[cidx]), OBJECT:([t1].[idx1]), SET:([t1].[c2] = [Expr1004]))
|--Compute Scalar(DEFINE:([Expr1013]=[Expr1013]))
|--Compute Scalar(DEFINE:([Expr1004]=[t1].[c2]+(1), [Expr1013]=CASE WHEN CASE WHEN ...
|--Top(ROWCOUNT est 0)
|--Clustered Index Seek(OBJECT:([t1].[cidx]), SEEK:([t1].[c1]=[@p1]) ORDERED FORWARD)
此索引“覆盖”查询“SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1”,这好像只是索引包含所有的查询引用的列的另外一种说法。SQL会使用这个索引替代[idx1]索引,因为基于此覆盖索引的查询计划是成本较低的。事实上。覆盖查询的索引意味着,针对聚集索引的书签查找不再是必须的。因为SELECT不再需要存取聚集索引,它不会被在聚集索引上的UPDATE锁诸塞。