To summarize the specifics: We need to stage approximately 5 million rows into a vendor (Oracle) database. Everything goes great for batches of 500k rows using OracleBulkCopy
(ODP.NET), but when we try to scale up to 5M, the performance starts slowing to a crawl once it hits the 1M mark, gets progressively slower as more rows are loaded, and eventually times out after 3 hours or so.
I suspect it's related to a primary key on the table, but I've been trawling the Oracle forums and Stack Overflow for information and a lot of what I'm reading contradicts that (also, a lot of posts seem to contradict each other). I'm hoping that somebody can set the record straight on some closely-related questions about the process:
Does the OracleBulkCopy
class use conventional or direct-path loading? Is there some way I can confirm this, one way or another?
Assuming it does use direct-path loading: Is it true that Oracle automatically sets all indexes to unusable during the load and puts them back online afterward? I've read several statements to this effect but again, cannot confirm it.
If #2 is true, then should it make any difference what indexes are on the table before I initiate a bulk copy operation? If so, why?
Related to #3, is there any practical difference, in general, between bulk loading with an unusable index vs. actually dropping the index before the load and recreating it afterward?
如果#2不正确,或者如果有一些我不理解的警告,那么在批量加载之前明确地使索引不可用,然后在之后 明确地重建它会有什么不同吗?
除了索引构建之外,还有什么可能导致批量复制操作随着添加的记录越来越多而逐渐变慢吗?(也许与日志记录有关,尽管我希望不会记录批量操作?)
如果除了首先删除 PK/索引之外真的没有其他方法可以使性能达到最佳状态,我可以采取哪些步骤来确保索引不会完全消失,即如果与数据库的连接丢失过程的中间?
Aar*_*ght 14
再过几天阅读和实验,我能够(大部分)回答其中的很多问题:
我发现这个埋在ODP.NET文档(具有讽刺意味的不是在OracleBulkCopy
文档):
ODP.NET 批量复制功能使用直接路径加载方法,这与 Oracle SQL*Loader 类似但不相同。使用直接路径加载比传统加载(使用传统 SQL
INSERT
语句)更快。
所以看起来它确实使用了直接路径。
我可以通过执行大型批量复制操作并从 SQL Developer 获取索引属性来验证这一点。在进行大容量复制时确实出现了索引UNUSABLE
。 但是,我还发现,OracleBulkCopy.WriteToServer
如果索引以某种状态启动,它将拒绝运行UNUSABLE
,因此显然这里还有更多事情要做,因为如果它像禁用和重建索引一样简单,那么它就不应该关心初始状态。
它确实有差别特别,如果该指数也是一个制约因素。在上面链接的文档中找到了这个小宝石:
启用约束
在 Oracle 批量复制期间,默认情况下会自动启用以下约束:
NOT NULL
UNIQUE
PRIMARY KEY
(非空列的唯一约束)
NOT NULL
在列数组构建时检查约束。任何违反NOT NULL
约束的行都会被拒绝。
UNIQUE
在加载结束时重建索引时验证约束。如果索引违反UNIQUE
约束,则该索引将处于索引不可用状态。
该文件是在发生什么事有点朦胧期间的负荷,尤其是主键,但有一点是绝对肯定的-它具有不同的行为与主键与没有之一。由于它很OracleBulkCopy
乐意允许您违反索引约束(并在完成后将索引置于UNUSABLE
状态),我的直觉是它在批量复制期间构建PK 索引,但只是在之后才对其进行验证。
我不确定观察到的差异是在 Oracle 内部还是只是OracleBulkCopy
. 陪审团仍然在这个问题上。
OracleBulkCopy
如果索引最初处于该UNUSABLE
状态,则会抛出异常,因此这确实是一个有争议的问题。
如果是其他因素,指标(尤其是PK指数)仍然是最重要的,因为我发现是:
创建一个具有相同架构的全局临时表(使用CREATE AS
),然后批量复制到临时表中,最后INSERT
从临时表到真实表中做一个普通的旧表。由于临时表没有索引,因此批量复制发生得非常快,最终INSERT
也很快,因为数据已经在表中(我还没有尝试追加提示,因为 5M 行的表到表复制已经用了不到 1 分钟)。
我还不确定 (ab) 以这种方式使用临时表空间的潜在后果,但到目前为止它没有给我带来任何麻烦,而且它比通过防止行损坏的替代方法安全得多或索引。
这样做的成功也很清楚地表明 PK 索引是问题所在,因为这是临时表和永久表之间唯一的实际区别 - 在性能测试期间都从零行开始。
结论:不要费心尝试使用 ODP.NET 将超过大约 100k 行大容量复制到索引 Oracle 表中。删除索引(如果您实际上不需要它)或将数据“预加载”到不同的(非索引)表中。