当执行带有SQL Server似乎的两个表的JOIN的SELECT语句时 单独锁定语句的两个表。例如通过类似的查询 这样:
SELECT ...
FROM
table1
LEFT JOIN table2
ON table1.id = table2.id
WHERE ...
我发现锁的顺序取决于WHERE条件。该 查询优化器尝试生成只读取尽可能多的执行计划 行必要。因此,如果WHERE条件包含table1列 它将首先从table1获取结果行,然后获取相应的行 table2中的行。如果列来自table2,它将以另一种方式执行 回合。更复杂的条件或索引的使用可能会产生影响 查询优化器的决定。
当一个语句读取的数据应该在事务中稍后更新 使用UPDATE语句,不保证UPDATE的顺序 语句匹配用于从2个表中读取数据的顺序。 如果另一个事务在事务更新时尝试读取数据 在执行SELECT语句时,它可能导致死锁 在UPDATE语句之间,因为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 = ?
两个表都表示类型层次结构,并且始终一起加载。所以 使用带有JOIN的SELECT加载对象是有意义的。加载两个表 单独不会给查询优化器找到最好的机会 执行计划。但是因为UPDATE语句只能更新一个表 在对象加载对象时,这会导致死锁 由另一个事务更新。对象的更新通常会导致UPDATE 两个表属于属于不同类型的对象的属性时 类型层次结构已更新。
我试图在SELECT语句中添加锁定提示,但事实并非如此 改变问题。它只会导致SELECT语句中的死锁 两个语句都试图锁定表,一个SELECT语句获取锁 与另一个陈述相反的顺序。也许有可能 加载更新数据始终使用相同的语句强制锁定 以相同的顺序。这样可以防止两个事务之间出现死锁 想要更新数据,但不会阻止只读取的事务 数据到死锁,需要有不同的WHERE条件。
唯一的工作,所以到目前为止这似乎是读取可能没有锁定 一点都不使用SQL Server 2005,可以使用SNAPSHOT ISOLATION完成此操作。该 SQL Server 2000的唯一方法是使用READ UNCOMMITED隔离 水平。
我想知道是否还有其他可能阻止SQL Server 导致这些死锁?
答案 0 :(得分:5)
当读者不阻止编写者时,在快照隔离下永远不会发生这种情况。除此之外,没有办法阻止这样的事情。我在这里写了很多repro脚本:Reproducing deadlocks involving only one table
编辑:
我无权访问SQL 2000,但我会尝试使用sp_getapplock序列化对象的访问,以便读取和修改永远不会同时运行。如果你不能使用sp_getapplock,请推出自己的互斥锁。
答案 1 :(得分:0)
解决此问题的另一种方法是将select ... from ... join拆分为多个select语句。将隔离级别设置为read committed。使用表变量来管理select中要连接到其他的数据。使用distinct将插入过滤到这些表变量中。
所以如果我有两个表A,B。我正在插入/更新到A然后B.在那里,sql的查询优化器更喜欢先读取B和A.我将把单个select分成2个选择。首先,我将阅读B.然后将此数据传递给下一个选择语句,该语句为A。
这里不会发生死锁,因为一旦第一个语句完成,表B上的读锁就会被释放。
PS我遇到了这个问题,这非常有效。比我的部队命令答案要好得多。
答案 2 :(得分:-1)
我遇到了同样的问题。使用查询提示FORCE ORDER将解决此问题。缺点是您无法利用查询优化器对查询的最佳计划,但这可以防止死锁。
所以(这是来自“Bill the Lizard”用户)如果你有一个查询FROM table1 LEFT JOIN table2而你的WHERE子句只包含table2中的列,执行计划通常会首先从table2中选择行,然后查找table1中的行。使用table2中的小结果集,只需要获取table1中的几行。使用FORCE ORDER首先必须获取table1中的所有行,因为它没有WHERE子句,然后连接table2中的行,并使用WHERE子句过滤结果。从而降低性能。
但如果您知道情况并非如此,请使用此功能。您可能希望手动优化查询。
语法是
SELECT ...
FROM
table1
LEFT JOIN table2
ON table1.id = table2.id
WHERE ...
OPTION (FORCE ORDER)