我最近遇到了一个奇怪的问题,在 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 行计数。