【问题标题】:Oracle Optimizer Awkwardly Doesn't Prefer to Use IndexOracle 优化器尴尬地不喜欢使用索引
【发布时间】:2018-09-22 21:40:15
【问题描述】:

我正在加入一个自身的表,但是虽然我希望这个操作使用索引,但它似乎没有。表上有 100 万条记录(MY_TABLE),我运行的查询正在执行大约 10000 条记录。(因此它低于整个表的 %1。)

测试用例:

explain plan for
SELECT *
  FROM SCHM.MY_TABLE A1, SCHM.MY_TABLE A2
 WHERE     (A1.K_ID = '123abc')
       AND A1.HDT = A2.HDT
       AND A2.C_DATE BETWEEN A1.SYSDATE - 0.0004 
                           AND A1.SYSDATE + 0.0004 
       AND A1.GKID = A2.GKID;


Plan hash value: 1210306805

----------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                  |     3 |    81 |    28   (0)| 00:00:01 |
|*  1 |  FILTER                               |                  |       |       |            |          |
|*  2 |   HASH JOIN                           |                  |     3 |    81 |    28   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| MY_TABLE          |     3 |    45 |     7   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN                  | IX_MY_TABLE_C_DATE |     3 |       |     4   (0)| 00:00:01 |
|   5 |    TABLE ACCESS BY INDEX ROWID BATCHED| MY_TABLE          |    17 |   204 |    21   (0)| 00:00:01 |
|*  6 |     INDEX RANGE SCAN                  | IX_MY_TABLE_K_ID |    17 |       |     4   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(SYSDATE@!+0.00004>=SYSDATE@!-0.00004)
   2 - access("A1"."HDT"="A2"."HDT" AND 
              "A1"."GKID"="A2"."GKID")
   4 - access("A2"."C_DATE">=SYSDATE@!-0.00004 AND 
              "A2"."C_DATE"<=SYSDATE@!+0.00004)
   6 - access("A1"."K_ID"=U'123abc')

在上面的语句中,可以看出使用了C_DATE上的索引。

但是,在下面的语句中,没有使用 C_DATE 上的索引。所以,查询运行很慢。

真实案例:

explain plan for
SELECT *
  FROM SCHM.MY_TABLE A1, SCHM.MY_TABLE A2
 WHERE     (A1.K_ID = '123abc')
       AND A1.HDT = A2.HDT
       AND A2.C_DATE BETWEEN A1.C_DATE - 0.0004 
                           AND A1.C_DATE + 0.0004 
       AND A1.GKID = A2.GKID;


Plan hash value: 1063167343

----------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                  |  4187K|   998M|  6549K  (1)| 00:04:16 |
|*  1 |  HASH JOIN                            |                  |  4187K|   998M|  6549K  (1)| 00:04:16 |
|   2 |   JOIN FILTER CREATE                  | :BF0000          |    17 |  2125 |    21   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| MY_TABLE          |    17 |  2125 |    21   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN                  | IX_MY_TABLE_K_ID |    17 |       |     4   (0)| 00:00:01 |
|   5 |   JOIN FILTER USE                     | :BF0000          |  1429M|   166G|  6546K  (1)| 00:04:16 |
|*  6 |    TABLE ACCESS STORAGE FULL          | MY_TABLE          |  1429M|   166G|  6546K  (1)| 00:04:16 |
----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access(A1.HDT=A2.HDT AND 
              A1.GKID=A2.GKID)
       filter(A2.C_DATE>=INTERNAL_FUNCTION(A1.C_DATE)-0.00004 AND 
              A2.C_DATE<=INTERNAL_FUNCTION(A1.C_DATE)+0.00004)
   4 - access(A1.K_ID=U'123abc')
   6 - storage(SYS_OP_BLOOM_FILTER(:BF0000,A2.HDT,A2.HDT))
       filter(SYS_OP_BLOOM_FILTER(:BF0000,A2.HDT,A2.HDT))

如果我使用提示/*+index(A2,IX_MY_TABLE_C_DATE )*/,一切都很好,使用索引并且查询运行得如我所愿。

真实案例中的查询无法更改,因为它是由应用程序创建的。

Index Information:
K_ID, not unique, position 1
HDT, not unique, position 1
C_DATE, not unique, position 1
ID Unique and Primary Key, position 1

为了在实际案例中使用索引,我必须进行哪些更改?

【问题讨论】:

  • 请包含表上存在的索引,以及主键。
  • 我添加索引和PK信息。
  • 我才意识到。你真的有一个名为“SYSDATE”的列吗?根据第一个查询的计划,在注释 #4 上,它与 SYSDATE(数据库上的日期)进行比较,而不是 A2 上的列。这比 JOIN 快得多。
  • 我没有名为 SYSDATE 的列。搜索 sysdate + 0.0004 和 sysdate - 0.0004 范围。
  • 您最近是否更新了表格的统计信息?

标签: sql oracle indexing query-performance


【解决方案1】:

嗯,第二个查询速度较慢,因为它与第一个查询完全不同。它在表之间有一个额外的连接:

AND A2.C_DATE BETWEEN A1.C_DATE - 0.0004 
                  AND A1.C_DATE + 0.0004

在一百万行上,这需要付出代价。

第一个查询没有这个连接条件,两个表都是:

  • 首先过滤。这使用索引很快:只有 3 行和 17 行。
  • 第二个加入他们。连接 3 行和 17 行不需要任何时间。

第二个查询需要执行:

  • 首先是一个巨大的(散列)连接,它可能返回 100K+ 行。
  • 稍后进行过滤。

这要慢得多。

我建议添加以下索引,然后再试一次:

create index ix_1 (k_id);
create index ix_2 (hdt, gkid, c_date);

【讨论】:

  • 但是如果我在 c_date 上运行带有索引提示的第二个查询(真实案例),它只返回 11 行。
  • 您可能只看到返回的 11 行(对应于计划中的 ID=0)。但是,在内部,HASH 连接需要在内存中组装 100K+ 行,然后再过滤它们(计划中的 ID=6)。工作量很大。
  • 我可以创建您建议的索引。但我想提请注意 gkid 只能是 2 种类型,而 hdt 只能是 150 种不同的类型。因此,如果您仍然建议,我将按照您的定义创建索引。
  • 是的,因为 hst 在索引中排在第一位,它会立即过滤掉 150。这是索引的第一列。
  • 索引提示执行时间为 219 毫秒
【解决方案2】:

您的自加入中有 3 个加入条件(HDT、GKID、C_DATE)和 1 个非加入条件 (K_ID)。所以对我来说,如果 DBMS 从匹配 K_ID 的记录开始,然后查找所有匹配的其他记录,这似乎很自然。

对于这种情况,我建议使用以下索引:

create index idx1 on my_table(k_id, hdt, gkid, c_date);
create index idx2 on my_table(hdt, gkid, c_date);

如果每个 k_id 只有几条记录,我相信 Oracle 会使用索引。如果有很多,Oracle 可能仍会使用第二个进行哈希连接。

【讨论】:

  • 根据跟踪文件,(K_ID) 的非连接条件仅返回 17 行。所以加入 c_date 的标准并没有太多的耦合
  • 我可以创建您建议的索引。但我想提请注意 gkid 只能是 2 种类型,而 hdt 只能是 150 种不同的类型。因此,如果您仍然建议,我将按照您的定义创建索引。
  • 听起来不错。可以快速找到 17 行,然后只需加入几行。索引似乎合适。祝你好运!
【解决方案3】:

只是补充一下,只要你有你的情况

  1. SQL 速度慢,
  2. 已经确定了一个提示 更快
  3. 无法更改 SQL,因为源不可用

那么这是 SQL 计划基线的完美案例。这些让您可以针对现有 SQL 锁定“好”计划,而无需触及 SQL 语句本身。

描述 SPM 的整个系列都在下面的链接中,但是“第 4 部分”的链接将详细介绍您想要实现的目标。

https://blogs.oracle.com/optimizer/sql-plan-management-part-1-of-4-creating-sql-plan-baselines https://blogs.oracle.com/optimizer/sql-plan-management-part-2-of-4-spm-aware-optimizer https://blogs.oracle.com/optimizer/sql-plan-management-part-3-of-4:-evolving-sql-plan-baselines https://blogs.oracle.com/optimizer/sql-plan-management-part-4-of-4:-user-interfaces-and-other-features

【讨论】:

    猜你喜欢
    • 2012-06-19
    • 2014-03-30
    • 2018-04-02
    • 2019-05-07
    • 2012-08-15
    • 2020-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多