7

我最近遇到了一个奇怪的问题,在 Oracle 数据库中编程:在可序列化事务中,我执行批量插入(INSERT ... SELECT),然后立即在更改后的表上打开一个带有 SELECT 的游标。我假设这个游标将包括新插入的行,但令我惊讶的是,它的内容是不稳定的,有时包括所有新插入的行,有时只包括一个子集。

我已经通过在打开光标之前提交解决了这个问题,但是这种行为让我感到困惑。在没有中间提交的情况下,在同一事务中插入之后的选择是否真的可以信任?或者这种行为是否与可序列化的事务有某种关系?

跟进:当尝试创建可重现的测试用例时,我只有在添加索引后才能获得这种行为(在这种情况下是主键索引,在实际代码上它是常规索引)。也许问题在于构建索引所花费的时间,以至于 SELECT 实际上使用不完整的索引来检索结果?无论如何,这里有一个可重现的测试用例:

-- Create empty source table
CREATE TABLE TEST_CASE_1 AS 
  (SELECT 'CONTENT' AS CONTENT
   FROM DUAL
   WHERE 1 = 2)

-- Add primary key
ALTER TABLE TEST_CASE_1
ADD CONSTRAINT TEST_CASE_1_PK PRIMARY KEY (CONTENT);

-- Create empty destination table
CREATE TABLE TEST_CASE_2 AS 
  (SELECT 'CONTENT' AS CONTENT
   FROM DUAL
   WHERE 1 = 2)

-- Example of faulty code
BEGIN

  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

  -- Populate with 100.000 rows (I used ALL_OBJECTS but any source of 100.000 rows is good)
  INSERT INTO TEST_CASE_1
    (SELECT ROWNUM 
     FROM ALL_OBJECTS
     WHERE ROWNUM <= 100000);

  INSERT INTO TEST_CASE_2
    (SELECT *
     FROM TEST_CASE_1
     WHERE CONTENT > 0);

  COMMIT;

END;

在这个例子中,我希望 TEST_CASE_2 也有 100.000 行。重现这个测试用例(在一个无负载的数据库中),我获得了大约 400-500 行插入。删除将事务设置为可序列化的语句,我获得了正确的 100.000 行计数。

4

2 回答 2

8

这似乎是一个错误;如果您可以访问 Oracle 的支持网站,请查看注释 1455175.1,它可以追溯到 8i。列出了几个错误编号(7592038 - 'SILENTLY INVISIBLE DATA FROM SELECT/UPDATE OF NEWLY INSERTED ROW IN SERIALIZABLE', 6363019)但它们作为 440317 的副本关闭('隔离级别可序列化导致在所选行上找不到数据AFTER INSERT'),它显示为仍处于打开状态并正在通过开发进行调查 - 即使它最初是针对版本 7(!)提出的。

您似乎是对的,这与PK有关。列出的解决方法是:

  • 提交执行到该点的工作。
  • 执行其他(但不同的)语句(可能在回滚到事务中较早建立的保存点之后)。
  • 回滚整个事务并从头开始重新启动事务。
  • 执行全表扫描并避免使用索引。

您知道第一个解决方法已经有效,我认为第二个或第三个对您没有帮助吗?您可以尝试第四个,/*+ FULL(TEST_CASE_1) */为第二个插入的选择添加提示。

我在 11.2.0.2 (Linux) 中没有收到错误,但我找不到任何表明该错误已得到修复的信息;而且我没有 11.1 环境来试用它 - 所以我无法检查最后一个解决方法是否适用于这个测试用例。

有一个说明,您可以在 11G 中获得 ORA-08177。如果我在创建表后过早地运行匿名块,或者如果我插入了太多行,我就会遇到这个问题,这似乎也与 PK 有关。这个先前的问题可能是相关的。

似乎这将继续成为一个问题,因此如果变通办法没有帮助,您可能需要重新考虑是否确实需要更改隔离级别;如果您这样做了,您可能需要向 Oracle 提出服务请求以获得更好的答案。

于 2012-08-07T12:40:09.160 回答
2

这是一个已确认的错误,Oracle 表示他们不打算修复它。以下是他们对我的服务请求的回复(2015 年 1 月)的摘录:

这些症状是由于在已知问题中发现了 Serializable 事务,并且您对 Bug 440317 的结论是正确的。

错误 440317 - 隔离级别可序列化导致在插入后选择的行上找不到数据
错误 16803610 - 使用 INSERT INTO 插入的行在可序列化隔离级别事务中丢失

这两个 Bug 都已发布,因此您可以在 MOS Bug 搜索中查看详细信息。

根据开发,对于历史很长的同一个问题存在多个错误。设计不容易更改,因此直到分叉出这个不是很有用的功能之前都没有解决办法。

开发已经关闭了错误说代码修复是不可行的。

建议的解决方法是
应用程序代码修改:
更改逻辑以在选择之前进行提交
或不使用可序列化
没有应用程序代码修改:
不要使用主键或表上的索引

于 2015-02-10T14:10:21.343 回答