SELECT JOIN语句与SQL Server引起的死锁

Reb*_*oot 8 sql-server select deadlock join sql-server-2000

当使用两个表的JOIN执行SELECT语句时,SQL Server似乎单独锁定语句的两个表.例如,通过这样的查询:

SELECT ...
FROM
    table1
    LEFT JOIN table2
        ON table1.id = table2.id
    WHERE ...
Run Code Online (Sandbox Code Playgroud)

我发现锁的顺序取决于WHERE条件.查询优化器尝试生成只根据需要读取尽可能多行的执行计划.因此,如果WHERE条件包含table1列,它将首先从table1获取结果行,然后从table2获取相应的行.如果列来自table2,它将以相反的方式执行.更复杂的条件或索引的使用也可能对查询优化器的决策产生影响.

当语句读取的数据应在事后使用UPDATE语句更新时,不能保证UPDATE语句的顺序与用于从2个表中读取数据的顺序相匹配.如果另一个事务在事务更新表时尝试读取数据,则在UPDATE语句之间执行SELECT语句时会导致死锁,因为SELECT既不能锁定第一个表,也不能UPDATE获取锁定第二个表.例如:

T1: SELECT ... FROM ... JOIN ...
T1: UPDATE table1 SET ... WHERE id = ?
T2: SELECT ... FROM ... JOIN ... (locks table2, then blocked by lock on table1)
T1: UPDATE table2 SET ... WHERE id = ?
Run Code Online (Sandbox Code Playgroud)

两个表都表示类型层次结构,并始终一起加载.因此,使用带有JOIN的SELECT加载对象是有意义的.单独加载两个表不会使查询优化器有机会找到最佳执行计划.但由于UPDATE语句一次只能更新一个表,因此在对象被另一个事务更新时加载对象时会导致死锁.当更新属于不同类型的类型层次结构的对象的属性时,对象的更新通常会导致两个表上的UPDATE.

我试图向SELECT语句添加锁定提示,但这不会改变问题.当两个语句都试图锁定表时,它只会导致SELECT语句中出现死锁,而一个SELECT语句以另一个语句的相反顺序获取锁.也许可以使用相同的语句加载数据以进行更新,从而强制锁定的顺序相同.这样可以防止两个想要更新数据的事务之间发生死锁,但不会阻止只读取数据的事务到需要具有不同WHERE条件的死锁.

到目前为止,这是唯一的工作,所以这似乎是读取可能根本没有锁定.使用SQL Server 2005,可以使用SNAPSHOT ISOLATION完成此操作.SQL Server 2000的唯一方法是使用READ UNCOMMITED隔离级别.

我想知道是否还有其他可能性来防止SQL Server导致这些死锁?

A-K*_*A-K 5

当读者不阻止编写者时,在快照隔离下永远不会发生这种情况.除此之外,没有办法阻止这样的事情.我在这里写了很多repro脚本:重现只涉及一个表的死锁

编辑:

我没有访问SQL 2000,但我会尝试使用sp_getapplock能够连续访问的对象,这样的阅读和修改从不同时运行.如果你不能使用sp_getapplock,请推出自己的互斥锁.