| 通过聚集索引的seek找到了一行,然后开始更新。这里注重的是,update的时候,它会申请一个针对clustered index的X锁的。 实际上到这里,我们就明白了为什么update会对select产生死锁。update的时候,会申请一个针对clustered index的X锁,这样就阻塞住了(注重,不是死锁!)select里面最后的那个clustered index seek。死锁的另一半在哪里呢?注重我们的select语句,c2存在于索引idx1中,c1是一个聚集索引cidx。问题就在这里!我们在p2中更新了c2这个值,所以sqlserver会自动更新包含c2列的非聚集索引:idx1。而idx1在哪里?就在我们刚才的select语句中。而对这个索引列的更改,意味着索引集合的某个行或者某些行,需要重新排列,而重新排列,需要一个X锁。 SO………,问题就这样被发现了。 总结一下,就是说,某个query使用非聚集索引来select数据,那么它会在非聚集索引上持有一个S锁。当有一些select的列不在该索引上,它需要根据rowid找到对应的聚集索引的那行,然后找到其他数据。而此时,第二个的查询中,update正在聚集索引上忙乎:定位、加锁、修改等。但因为正在修改的某个列,是另外一个非聚集索引的某个列,所以此时,它需要同时更改那个非聚集索引的信息,这就需要在那个非聚集索引上,加第二个X锁。select开始等待update的X锁,update开始等待select的S锁,死锁,就这样发生鸟。 那么,为什么我们增加了一个非聚集索引,死锁就消失鸟?我们看一下,按照上文中自动增加的索引之后的执行计划: SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1 1 |--Index Seek(OBJECT:([deadlocktest].[dbo].[t1].[_dta_index_t1_7_2073058421__K2_K1_3]), SEEK:([deadlocktest].[dbo].[t1].[c2] >= [@p1] AND [deadlocktest].[dbo].[t1].[c2] <= [@p1] (1)) ORDERED FORWARD) 哦,对于clustered index的需求没有了,因为增加的覆盖索引已经足够把所有的信息都select出来。就这么简单。 实际上,在sqlserver 2005中,假如用profiler来抓eventid:1222,那么会出现一个死锁的图,很直观的说。 下面的方法,有助于将死锁减至最少(具体情况,请看SQLServer联机帮助,搜索:将死锁减至最少即可。 按同一顺序访问对象。 避免事务中的用户交互。 保持事务简短并处于一个批处理中。 使用较低的隔离级别。 使用基于行版本控制的隔离级别。 将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON,使得已提交读事务使用行版本控制。 使用快照隔离。 使用绑定连接。
|
| 共3页: 上一页 [1] [2] 3 下一页 |
评论加载中…