Oracle 11G - 插入索引的性能影响

mon*_*mon 5 oracle indexing insert database-performance oracle11gr2

客观的

验证插入没有 PK/index 的记录加上 create thme 是否比使用 PK/Index 插入快。

注意
这里的重点不是索引需要更多时间(很明显),而是总成本(不带索引的插入+创建索引)高于(带索引的插入)。因为我被教导在没有索引的情况下插入并稍后创建索引,因为它应该更快。

环境

戴尔 Latitude 核心 i7 2.8GHz 8G 内存和 SSD 硬盘
Oracle 11G R2 64 位上的 Windows 7 64 位

背景

我被教导在没有 PK/索引的情况下插入记录并在插入后创建它们会比使用 PK/索引插入更快。

然而,使用 PK/Index 插入 100 万条记录实际上比稍后创建 PK/Index 更快,大约 4.5 秒 vs 6 秒,下面的实验。通过将记录增加到 300 万(999000 -> 2999000),结果是一样的。

状况

  • 表 DDL 如下。一个用于数据和索引的大文件表空间。
    (测试了一个单独的索引表空间,结果相同,整体性能较差)
  • 每次运行前冲洗缓冲液/线轴。
  • 每次运行实验 3 次,并确保结果相似。

要刷新的 SQL:

ALTER SYSTEM CHECKPOINT;
ALTER SYSTEM FLUSH SHARED_POOL;
ALTER SYSTEM FLUSH BUFFER_CACHE;
Run Code Online (Sandbox Code Playgroud)

“稍后插入 PK/Index + PK/Index 创建”比“插入 PK/Index”是否真的是真的?

我是否在实验中犯了错误或遗漏了某些条件?

插入带有 PK/Index 的记录

TRUNCATE TABLE TBL2;
ALTER TABLE TBL2 DROP CONSTRAINT PK_TBL2_COL1 CASCADE;
ALTER TABLE TBL2 ADD  CONSTRAINT PK_TBL2_COL1 PRIMARY KEY(COL1) ;

SET timing ON
INSERT INTO TBL2
SELECT i+j, rpad(TO_CHAR(i+j),100,'A')
FROM (
  WITH DATA2(j) AS (
      SELECT 0 j FROM DUAL
      UNION ALL
      SELECT j+1000 FROM DATA2 WHERE j < 999000
  )
  SELECT j FROM DATA2
),
(
  WITH DATA1(i) AS (
      SELECT 1 i FROM DUAL
      UNION ALL
      SELECT i+1 FROM DATA1 WHERE i < 1000
  )
  SELECT i FROM DATA1
);
commit;

1,000,000 rows inserted.
Elapsed: 00:00:04.328 <----- Insert records with PK/Index
Run Code Online (Sandbox Code Playgroud)

插入没有 PK/Index 的记录并在之后创建它们

TRUNCATE TABLE TBL2;
ALTER TABLE &TBL_NAME DROP CONSTRAINT PK_TBL2_COL1 CASCADE;

SET TIMING ON
INSERT INTO TBL2
SELECT i+j, rpad(TO_CHAR(i+j),100,'A')
FROM (
  WITH DATA2(j) AS (
      SELECT 0 j FROM DUAL
      UNION ALL
      SELECT j+1000 FROM DATA2 WHERE j < 999000
  )
  SELECT j FROM DATA2
),
(
  WITH DATA1(i) AS (
      SELECT 1 i FROM DUAL
      UNION ALL
      SELECT i+1 FROM DATA1 WHERE i < 1000
  )
  SELECT i FROM DATA1
);
commit;
ALTER TABLE TBL2 ADD CONSTRAINT PK_TBL2_COL1 PRIMARY KEY(COL1) ;

1,000,000 rows inserted.
Elapsed: 00:00:03.454 <---- Insert without PK/Index

table TBL2 altered.
Elapsed: 00:00:02.544 <---- Create PK/Index
Run Code Online (Sandbox Code Playgroud)

表 DDL

CREATE TABLE TBL2 (
    "COL1" NUMBER,
    "COL2" VARCHAR2(100 BYTE),
    CONSTRAINT "PK_TBL2_COL1" PRIMARY KEY ("COL1")
) TABLESPACE "TBS_BIG" ;
Run Code Online (Sandbox Code Playgroud)

Dav*_*dge 1

确实,如果您不必同时修改一个或多个索引并可能执行约束检查,则修改表的速度会更快,但如果您随后必须添加这些索引,则这在很大程度上也是无关紧要的。您必须考虑对您希望实现的系统的整体更改,而不仅仅是其中的单个部分。

显然,如果您要向已经包含数百万行的表中添加一行,那么删除并重建索引将是愚蠢的。

但是,即使您有一个完全空的表,并且要向其中添加几百万行,将索引推迟到之后仍然会比较慢。

原因是这样的插入最好使用直接路径机制来执行,并且当您使用直接路径插入到带有索引的表中时,会构建临时段,其中包含构建索引所需的数据(数据加上 rowids )。如果这些临时段比您刚刚加载的表小得多,那么扫描和构建索引的速度也会更快。

另一种方法是,如果表上有五个索引,则在加载表后进行五次全表扫描以构建索引。

显然,这里涉及巨大的灰色地带,但做得很好:

  1. 质疑权威和一般经验法则,以及
  2. 进行实际测试以确定您自己案例中的事实。

编辑:

进一步的考虑——您在删除索引时运行备份。现在,在紧急恢复之后,当您需要业务来恢复系统时,您必须有一个脚本来验证所有索引是否都已到位。

另外,如果您绝对决定在批量加载期间不维护索引,请不要删除索引——而是禁用它们。这保留了索引存在和定义的元数据,并允许更简单的重建过程。请注意,不要意外地通过截断表来重新启用索引,因为这将使禁用的索引再次启用。