代码之家  ›  专栏  ›  技术社区  ›  Reboot

SQL Server的SELECT JOIN语句导致死锁

  •  7
  • Reboot  · 技术社区  · 14 年前

    SELECT ...
    FROM
        table1
        LEFT JOIN table2
            ON table1.id = table2.id
        WHERE ...
    

    我发现锁的顺序取决于位置条件。这个 查询优化器试图生成一个执行计划,该计划只读取 根据需要排列。如果WHERE条件包含table1的一列 它将首先从表1中获取结果行,然后获取相应的 表2中的行。如果列来自表2,它将以另一种方式执行 圆圆的。更复杂的条件或使用索引可能会对 查询优化器的决定也是如此。

    对于UPDATE语句,不能保证更新的顺序 语句与用于从2个表中读取数据的顺序匹配。 如果另一个事务在更新数据时尝试读取数据 在中执行SELECT语句时,它可能导致死锁 在UPDATE语句之间,因为SELECT和 更新也不能获得第二个表上的锁。为了

    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 = ?
    

    两个表都表示一个类型层次结构,并且总是一起加载。所以它 单独使用不会给查询优化器一个机会来找到最好的 当加载对象时,这会导致死锁 由另一个事务更新。对象的更新通常会导致 当对象的属性属于不同类型的

    我曾尝试向SELECT语句添加锁定提示,但没有 改变问题。当 以与另一个陈述相反的顺序。也许有可能 以同样的顺序。这将防止两个 希望更新数据,但不会阻止只读取 数据到死锁需要有不同的WHERE条件。

    到目前为止,唯一的一个解决办法似乎是读取可能无法获得锁 水平。

    3 回复  |  直到 14 年前
        1
  •  5
  •   A-K    14 年前

    在快照隔离下,当读卡器不阻止写入程序时,这种情况永远不会发生。除此之外,没有办法阻止这样的事情。我在这里写了很多复制脚本: Reproducing deadlocks involving only one table

    编辑:

    我没有访问SQL2000的权限,但是我会尝试通过使用sp\u getapplock序列化对对象的访问,这样读取和修改就不会同时运行。如果您不能使用sp\u getapplock,请推出您自己的互斥锁。

        2
  •  0
  •   Ankush    13 年前

    另一种解决方法是拆分选择。。。来自。。。加入多个select语句。将隔离级别设置为read committed。使用表变量将数据从选定对象输送到其他对象。使用distinct筛选这些表变量中的插入。

        3
  •  -1
  •   Ankush    13 年前

    我也面临同样的问题。使用查询提示FORCE ORDER将解决此问题。缺点是您无法利用查询优化器为您的查询提供的最佳计划,但这将防止死锁。

    但如果你知道不会这样,就用这个。您可能需要手动优化查询。

    SELECT ...
    FROM
        table1
        LEFT JOIN table2
            ON table1.id = table2.id
        WHERE ...
    OPTION (FORCE ORDER)