简体   繁体   English

Oracle:在可序列化事务中插入后立即选择

[英]Oracle: Select immediately after insert in serializable transaction

I have run into a strange problem lately, programming in an Oracle database: inside a serializable transaction, i do a mass insert (INSERT ... SELECT), and immediately after, I open a cursor with a SELECT on the altered table. 我最近遇到了一个奇怪的问题,在Oracle数据库中编程:在一个可序列化的事务中,我做了一个大量插入(INSERT ... SELECT),然后立即打开一个带有SELECT的变量表上的游标。 I assumed that this cursor would include the newly inserted rows, but, to my surprise, its contents are erratic, sometimes including all the newly inserted rows, and sometimes only a subset. 我假设这个游标将包含新插入的行,但令我惊讶的是,它的内容不稳定,有时包括所有新插入的行,有时只包含一个子集。

I have solved this problem by commiting before opening the cursor, but the behaviour has puzzled me. 我通过在打开光标之前提交解决了这个问题,但这种行为让我感到困惑。 Can a select after an insert inside the same transaction, without an intervining commit, actually be trusted? 可以在同一事务中插入后进行选择,而不进行干预提交,实际上是否可信? Or is this behaviour somehow related to the transaction being serializable? 或者这种行为是否与可序列化的事务有关?

Followup : When trying to create a reproducible test case, I was only able to obtain this behaviour once I added an index (in this case a primary key index, on the actual code it was a regular index). 后续 :当我尝试创建一个可重现的测试用例时,我只能在添加索引后获得此行为(在这种情况下是主键索引,在实际代码中它是常规索引)。 Perhaps the problem lies in the time spent building the index, so that the SELECT actually uses an incomplete index to retrieve the results? 也许问题在于构建索引所花费的时间,因此SELECT实际上使用不完整的索引来检索结果? Anyway, here goes a reproducible test case: 无论如何,这是一个可重现的测试用例:

-- 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;

In this example, I would expect TEST_CASE_2 to also have 100.000 rows. 在这个例子中,我希望TEST_CASE_2也有100.000行。 Reproducing this test case (in a load-free database), I obtained about 400-500 rows inserted. 再生这个测试用例(在无负载数据库中),我获得了大约400-500行插入。 Removing the statement setting the transaction as serializable, I obtained the correct 100.000 row count. 删除将事务设置为可序列化的语句,我获得了正确的100.000行计数。

This seems to be a bug; 这似乎是一个错误; if you've got access to Oracle's support website look at note 1455175.1, which dates back to 8i. 如果您可以访问Oracle的支持网站,请查看注释1455175.1,该注释可以追溯到8i。 There are a couple of bug numbers listed (7592038 - 'SILENTLY INVISIBLE DATA FROM SELECT/UPDATE OF NEWLY INSERTED ROW IN SERIALIZABLE', 6363019) but they're closed as duplicates of 440317 ('ISOLATION LEVEL SERIALIZABLE CAUSES NO DATA FOUND ON ROWS SELECTED AFTER INSERT'), which is shown as still open and being investigated by development - even though it was originally raised against version 7(!). 列出了几个错误号码(7592038 - “可选择的最新信息/可以在SERIALIZABLE中新插入的行的最新信息”,6363019)但是它们被关闭为440317的重复('隔离级别可串行化导致没有数据在所选择的行上找到AFTER INSERT'),显示为仍处于打开状态并通过开发进行调查 - 即使它最初是针对版本7(!)而提出的。

You seem to be right that's it's related to the PK. 你似乎是正确的,它与PK有关。 The workarounds listed are: 列出的变通方法是:

  • Commit the work executed to that point. 将执行的工作提交到那一点。
  • Execute additional (but different) statements (perhaps after rolling back to a savepoint established earlier in the transaction). 执行其他(但不同的)语句(可能在回滚到事务中先前建立的保存点之后)。
  • Roll back the entire transaction and restart the transaction from beginning. 回滚整个事务并从头开始重新启动事务。
  • Perform a full table scan and avoid using the indexes. 执行全表扫描并避免使用索引。

You know the first workaround is effective already, and I don't think the second or third will help you? 您知道第一个解决方法已经有效,我不认为第二个或第三个会对您有所帮助吗? You could try the fourth, adding a /*+ FULL(TEST_CASE_1) */ hint to the select for the second insert. 您可以尝试第四次,为第二次插入的select添加/*+ FULL(TEST_CASE_1) */提示。

I don't get the error in 11.2.0.2 (Linux), though I can't find anything suggesting the bug has been fixed; 我没有收到11.2.0.2(Linux)中的错误,但我找不到任何暗示错误已修复的内容; and I don't have an 11.1 environment to try it on - so I can't check that last work-around applies to this test case. 并且我没有11.1环境可以尝试 - 因此我无法检查上次解决方案是否适用于此测试用例。

There's a note that you can get ORA-08177 instead in 11G. 有一个注意事项,您可以在11G中获得ORA-08177。 I had that problem if I ran the anonymous block too soon after creating the tables, or if I had too many rows inserted, which also seems to be related to the PK. 如果我在创建表后过早地运行匿名块,或者如果我插入的行太多,那么我就遇到了这个问题,这似乎与PK有关。 This previous question may be relevant. 之前的问题可能是相关的。

Seems like this will continue to be a problem, so if the workarounds don't help you may need to reconsider if you really do need to change the isolation level; 似乎这将继续是一个问题,所以如果解决方法没有帮助,您可能需要重新考虑是否确实需要更改隔离级别; and if you do you may have to raise a service request with Oracle to get a better answer. 如果你这样做,你可能不得不向Oracle提出服务请求以获得更好的答案。

This is a confirmed bug, and Oracle stated that they do not plan on fixing it. 这是一个确认的错误,Oracle表示他们不打算修复它。 Here's an excerpt from their response to my service request (January 2015): 以下是他们对我的服务请求的回复摘录(2015年1月):

These symptoms are due to the Serializable transaction been found with the known issues and you conclusion with the Bug 440317 is correct. 这些症状是由于已发现已知问题的Serializable事务而您使用Bug 440317得出的结论是正确的。

Bug 440317 - ISOLATION LEVEL SERIALIZABLE CAUSES NO DATA FOUND ON ROWS SELECTED AFTER INSERT 错误440317 - 隔离级别可串行化导致在插入后选择的行中找不到任何数据
Bug 16803610 - ROWS INSERTED USING INSERT INTO ARE LOST IN SERIALIZABLE ISOLATION LEVEL TRANSAC 错误16803610 - 使用插入插入的行在可串行隔离级别转换中丢失

Both these Bugs are published, so you can see the details in the MOS bug search. 这两个错误都已发布,因此您可以在MOS错误搜索中查看详细信息。

As per development, there was multiple bugs for the same issue with very long history. 根据开发情况,同一个问题有很多错误,历史很长。 The design isn't easy to change, hence there's no fix till the moment forking out this feature a not very useful one. 设计不容易改变,因此直到分配这个功能的时刻还没有解决,这个功能并不是很有用。

Development has closed the bug saying code fix is not feasible. 开发已经关闭了错误,说代码修复是不可行的。

The workarounds suggested are 建议的解决方法是
Application Code modifiction: 应用代码修改:
change the logic to have the commit before the select 在select之前更改逻辑以进行提交
or dont use serializable 或者不要使用serializable
Without application code modification: 没有应用代码修改:
Do not use the primary key or the indexes on the table 不要使用表上的主键或索引

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM