当长时间运行的查询过程中插入一行数据会发生什么?

3
我正在编写一些数据加载代码,从Oracle数据库中的一个大而慢的表中提取数据。我只能以只读方式访问数据,并且无法更改索引或以任何方式影响查询速度。
我的选择语句需要5分钟才能执行,并返回约300,000行。系统不断插入大批新记录,我需要确保我获得每一个最后一个记录,因此我需要保存上次下载数据的时间戳。
我的问题是:如果我的选择语句运行了5分钟,并且在选择运行时插入了新行,那么在查询结果中我会收到新行吗?
我的直觉告诉我答案是否定的,特别是考虑到这5分钟中很大一部分时间都是用于从数据库传输数据到本地环境,但我找不到有关该场景的直接文档说明。

4
阅读有关多版本读一致性和隔离级别的内容,特别是语句级读一致性。但是需要注意批处理被插入和被提交的时间差异,并了解如果您的时间戳落在该间隙中会发生什么。 - Alex Poole
没错。我正在尝试确定是否可以从我开始select的时间戳开始,或者在select返回后,或者如果两者都不起作用,我需要使用其他策略来防止同时发生插入和选择。 - Jrud
1个回答

8
如果我的select语句运行了5分钟,而在运行select时插入了新行,那么我在查询结果中是否会收到这些新行?
不会。Oracle实施严格的隔离级别,并且不允许脏读取。默认的隔离级别是Read Committed。这意味着你在五分钟后得到的结果集将与Oracle可以在0.0000001秒内向您提供所有记录的结果相同。查询开始运行后提交的任何内容都将不包括在结果中。这包括对记录的更新以及插入。
Oracle通过跟踪UNDO表空间中表的更改来实现这一点。只要它可以限制该数据的原始图像,你的查询就会运行完成;如果由于任何原因撤消信息被覆盖,则查询将失败并显示可怕的ORA-1555:快照过旧。没错:Oracle宁愿抛出异常也不会提供不一致的结果集。
请注意,此一致性适用于语句级别。如果我们在一个事务中两次运行相同的查询,我们可能会看到两个不同的结果集。如果这是一个问题(我认为在您的情况下不是),我们需要从Read Committed切换到Serialized隔离。
概念手册详细介绍了并发和一致性。了解更多信息。 因此,回答您的问题,请从开始选择的时间戳取值。具体来说,请在启动查询之前从表中获取max(created_ts)。这应该可以保护您免受Alex提到的间隙的影响(如果记录插入时没有立即提交,则有可能丢失记录,如果您基于与系统时间戳的比较进行选择)。尽管这样做意味着你在同一个事务中发出两个查询,这意味着你仍需要Serialized隔离!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接