【发布时间】:2012-02-09 17:55:29
【问题描述】:
如果两个并发的DML语句修改相同的数据并使用相同的访问方法,是否可能发生死锁?
根据我的测试以及我对 Oracle 工作原理的猜测,答案是否定的。
但我想 100% 确定。我正在寻找表明死锁不会以这种方式发生的官方消息来源,或者是证明死锁可以以这种方式发生的测试用例。
问这个问题的另一种方式是:如果使用相同的访问方法,Oracle 是否总是以相同的顺序返回结果? (并且运行之间没有数据变化。)
例如,如果查询使用全表扫描并以 4/3/2/1 的顺序返回行,它会总是按该顺序返回行吗?如果索引范围扫描以 1/2/3/4 的顺序返回行,它会总是按该顺序返回行吗?实际顺序是什么并不重要,顺序是确定性的。
(并行性可能会给这个问题增加一些复杂性。语句的整体顺序会因许多因素而异。但是对于锁定,我相信只有每个并行会话中的顺序很重要。再说一次,我的测试表明顺序是确定性的,不会导致死锁。)
更新
我最初的问题有点笼统。我最感兴趣的是,是否可以同时在两个不同的会话中运行update table_without_index set a = -1 之类的东西,然后陷入僵局? (我问的是单个更新,而不是一系列更新。)
首先,让我证明完全相同的语句会导致死锁。
创建表、索引和一些数据:
为简单起见,我只更新同一列。在现实世界中会有不同的列,但我认为这不会改变任何事情。
请注意,我使用 pctfree 0 创建表,更新后的值会占用更多空间,因此会有大量行迁移。 (这是对@Tony Andrew 的回答,尽管我担心我的测试可能过于简单。另外,我认为我们不需要担心在更新之间插入行;只有一个更新会看到新行,所以它不会导致死锁。除非新行也移动了一堆其他东西。)
drop table deadlock_test purge;
create table deadlock_test(a number) pctfree 0;
create index deadlock_test_index on deadlock_test(a);
insert into deadlock_test select 2 from dual connect by level <= 10000;
insert into deadlock_test select 1 from dual connect by level <= 10000;
commit;
在会话 1 中运行此块:
begin
while true loop
update deadlock_test set a = -99999999999999999999 where a > 0;
rollback;
end loop;
end;
/
在会话 2 中运行此块:
--First, influence the optimizer so it will choose an index range scan.
--This is not gaurenteed to work for every environment. You may need to
--change other settings for Oracle to choose the index over the table scan.
alter session set optimizer_index_cost_adj = 1;
begin
while true loop
update deadlock_test set a = -99999999999999999999 where a > 0;
rollback;
end loop;
end;
/
几秒钟后,其中一个会话抛出 ORA-00060: deadlock detected while waiting for resource。这是因为同一查询在每个会话中以不同的顺序锁定行。
排除上述情况,是否会发生死锁?
以上演示了执行计划的更改可能导致死锁。 但是即使执行计划保持不变,也会发生死锁吗?
据我所知,如果您删除optimizer_index_cost_adj 或其他任何会改变计划的内容,代码将永远不会导致死锁。 (我已经运行了一段时间的代码,没有任何错误。)
我问这个问题是因为我正在开发的系统偶尔会发生这种情况。它还没有失败,但我们想知道它是否真的安全,或者我们是否需要在更新周围添加额外的锁定?
有人可以构建一个测试用例,其中单个更新语句同时运行并使用相同的计划会导致死锁吗?
【问题讨论】:
-
不确定关于 DML 死锁的第一部分与关于查询结果排序的第二部分有何相同之处,但对于问题 2,您需要在查询中使用“ORDER BY”以保证行按特定顺序排列。
-
@tbone 我希望我的更新使连接更清晰。 (这是一个复杂的问题,希望我解释的准确。)如果两个查询尝试同时更新表中的所有行,但顺序不同,则可能会出现死锁。当一个查询使用索引而另一个查询使用 FTS 时,很容易看到这种情况。但它可以仅通过 FTS 或仅索引范围扫描发生吗?这取决于这些操作是否以相同的顺序处理行。
标签: oracle