Oracle:大幅提升查询性能

Posted

技术标签:

【中文标题】Oracle:大幅提升查询性能【英文标题】:Oracle: hugely improve query performance 【发布时间】:2015-10-17 14:12:13 【问题描述】:

我正在尝试提高性能的下一个查询:

select atx.journal_id
    ,ab.c_date
from acct_batch ab 
    join acct_tx atx on ab.acct_id = atx.acct_id 
      and ab.batch_id = atx.batch_id
    join journal j on j.journal_id = atx.journal_id
      and j.journal_type_id = 6
    join acct a on a.acct_id = atx.acct_id 
      and a.acct_type_id = 32
    join payments p on p.payment_id = j.payment_id
    join routing r on r.route_id = p.route_id 
      and r.acq_code = 'RZ_NS'
    join acq_acct aa on aa.acq_code = r.acq_code
      and aa.acq_acct_code = r.acq_acct_code
      and aa.slc = 'MXM'
where ab.c_date between to_date(to_char('01-JUL-2015')) and  last_day(sysdate);

我跑过并查看了解释计划,总成本为 7388。其中,最昂贵的部分是与 journal 表的联接,成本为 6319。

该表大约有 160 万行,有 87 个分区,其中只有两个包含这些行(分区 6 有 140 万行,分区 12 有大约 20 万行的其余部分)。

我尝试的第一件事是重新编写查询以避免在将实际 journal_type_id 匹配为 6 时进行全扫描,但我想我的理解是不正确的,因为成本仍然是 7388。

select atx.journal_id
    ,ab.c_date
from acct_batch ab 
    join acct_tx atx on ab.acct_id = atx.acct_id 
      and ab.batch_id = atx.batch_id
    join (select 
              journal_id
              , payment_id 
          from journal 
          where journal_type_id = 6) j on j.journal_id = atx.journal_id
    join acct a on a.acct_id = atx.acct_id 
      and a.acct_type_id = 32
    join payments p on p.payment_id = j.payment_id
    join routing r on r.route_id = p.route_id 
      and r.acq_code = 'RZ_NS'
    join acq_acct aa on aa.acq_code = r.acq_code
      and aa.acq_acct_code = r.acq_acct_code
      and aa.slc = 'MXM'
where ab.c_date between to_date(to_char('01-JUL-2015')) and  last_day(sysdate);

我确实寻找了很多资源,决定我重写查询的原因之一是this video。

我仍在积极寻找提高性能的方法,但我想我会在这里提出一个问题,也许可以得到一些提示。

我认为视频中的人所说的第一件事是确定哪个是您的“驱动表”(根据键确定选择哪些行的那个),所以我我目前正在寻找一种重写查询的方法,以尽可能地识别和使用这个驱动表及其索引。

我不知道我是否走在正确的轨道上,但如果你认为我应该继续前进,请阻止我。另外,请注意,我是性能调优的初学者,实际上这是我的第一次。

感谢任何帮助。

更新:

包含查询中使用的列的一些索引是:

╔════════════╦═══════════════╦════════════╦═══════════╦═════════════╦═══════════════════════════════════╗
║   Table    ║   IndexName   ║ Uniqueness ║ IndexType ║ Partitioned ║              Columns              ║
╠════════════╬═══════════════╬════════════╬═══════════╬═════════════╬═══════════════════════════════════╣
║ Acct_Batch ║ Acct_Batch_PK ║ UNIQUE     ║ NORMAL    ║ NO          ║ Acct_ID, Batch_ID                 ║
║ Acct_TX    ║ Acct_TX_IDX   ║ NONUNIQUE  ║ NORMAL    ║ YES         ║ Acct_ID, Batch_ID                 ║
║ Acct_TX    ║ Acct_TX_BIDX  ║ NONUNIQUE  ║ NORMAL    ║ YES         ║ Journal_ID, Acct_ID               ║
║ Journal    ║ Journal_PK    ║ UNIQUE     ║ NORMAL    ║ YES         ║ Journal_ID                        ║
║ Journal    ║ JType_BIDX    ║ NONUNIQUE  ║ NORMAL    ║ YES         ║ Journal_Type_ID, Book_Date        ║
║ Journal    ║ JType_BIDX_2  ║ NONUNIQUE  ║ NORMAL    ║ YES         ║ MCODE, Journal_Type_ID, Book_Date ║
║ Journal    ║ JPay_BIDX     ║ NONUNIQUE  ║ NORMAL    ║ YES         ║ Payment_ID, Journal_ID            ║
╚════════════╩═══════════════╩════════════╩═══════════╩═════════════╩═══════════════════════════════════╝

如果您需要查看有关其他表的更多索引或详细信息,请告诉我。

示例说明计划:

-------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                 | Name              | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                          |                   |     1 |   160 |  7388   (1)| 00:01:29 |       |       |
|*  1 |  FILTER                                   |                   |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                            |                   |       |       |            |          |       |       |
|   3 |    NESTED LOOPS                           |                   |     1 |   160 |  7388   (1)| 00:01:29 |       |       |
|*  4 |     HASH JOIN                             |                   |     4 |   604 |  7380   (1)| 00:01:29 |       |       |
|   5 |      NESTED LOOPS                         |                   |       |       |            |          |       |       |
|   6 |       NESTED LOOPS                        |                   |   107 | 14338 |  7372   (1)| 00:01:29 |       |       |
|*  7 |        HASH JOIN                          |                   |    27 |  3186 |  7298   (1)| 00:01:28 |       |       |
|   8 |         NESTED LOOPS                      |                   |       |       |            |          |       |       |
|   9 |          NESTED LOOPS                     |                   |   102 | 10302 |   978   (0)| 00:00:12 |       |       |
|  10 |           NESTED LOOPS                    |                   |    11 |   638 |    37   (0)| 00:00:01 |       |       |
|* 11 |            TABLE ACCESS BY INDEX ROWID    | ACQ_ACCT          |    11 |   253 |     4   (0)| 00:00:01 |       |       |
|* 12 |             INDEX RANGE SCAN              | AA_PK             |    16 |       |     2   (0)| 00:00:01 |       |       |
|  13 |            TABLE ACCESS BY INDEX ROWID    | ROUTES            |     1 |    35 |     3   (0)| 00:00:01 |       |       |
|* 14 |             INDEX RANGE SCAN              | R_A_BIDX          |     1 |       |     2   (0)| 00:00:01 |       |       |
|  15 |           PARTITION RANGE ALL             |                   |    95 |       |    84   (0)| 00:00:02 |     1 |    84 |
|* 16 |            INDEX RANGE SCAN               | P_R_ID_BIDX       |    95 |       |    84   (0)| 00:00:02 |     1 |    84 |
|  17 |          TABLE ACCESS BY LOCAL INDEX ROWID| PAYMENTS          |     9 |   387 |   100   (0)| 00:00:02 |     1 |     1 |
|  18 |         PARTITION RANGE ALL               |                   |   107K|  1782K|  6319   (1)| 00:01:16 |     1 |    87 |
|* 19 |          TABLE ACCESS FULL                | JOURNAL           |   107K|  1782K|  6319   (1)| 00:01:16 |     1 |    87 |
|  20 |        PARTITION RANGE ITERATOR           |                   |     4 |       |     2   (0)| 00:00:01 |   KEY |   KEY |
|* 21 |         INDEX RANGE SCAN                  | ATX_A_IDX         |     4 |       |     2   (0)| 00:00:01 |   KEY |   KEY |
|  22 |       TABLE ACCESS BY LOCAL INDEX ROWID   | ACCT_TX           |     4 |    64 |     3   (0)| 00:00:01 |     1 |     1 |
|* 23 |      INDEX RANGE SCAN                     | AB_B_A_IDX        |  5006 | 85102 |     8   (0)| 00:00:01 |       |       |
|* 24 |     INDEX UNIQUE SCAN                     | ACC_PK            |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 25 |    TABLE ACCESS BY INDEX ROWID            | ACCT              |     1 |     9 |     2   (0)| 00:00:01 |       |       |
-------------------------------------------------------------------------------------------------------------------------------

【问题讨论】:

添加表格索引会有所帮助。 @JorgeCampos 我正在调查JOINs 中的列使用了哪些索引,但我没有找到任何关于它是什么类型的索引的信息(仅当它是复合索引以及它是否唯一)。我也是 Oracle 的初学者,我对 SQL Server 的背景稍有经验,所以.. 我正在尝试查找有关索引类型的任何信息:集群、非集群、堆等。如果有,请告诉我这很有用,或者它甚至存在于 Oracle 中.. 分区中的键是哪一列?并附上第一次查询的完整计划。 @ArkadiuszŁukasiewicz 我怎么能看到这个?我似乎在 SQL Developer 中找不到它.. 从 USER_PART_KEY_COLUMNS 中选择 *。 【参考方案1】:

首先检查您的统计信息是否已更新:优化器严重依赖统计信息! 其次,您应该说一下您使用此查询获得的行数:根据每个条件选择的行数,完全扫描可能比索引搜索更好。

【讨论】:

我刚刚读到一些关于优化器的东西,决定有时全扫描更适合读取大块数据,从我的解释计划中可以看出,JOURNAL 表上的连接/条件返回 107k行。所以,我不确定可以为此做些什么。m 本次查询输出的行数为20。 我记得当结果超过表的 5% 时,表扫描更方便。您可以强制使用索引,但性能会最差。 好的,那么什么是好的方法呢?寻找什么 JOIN 或条件过滤掉最多的记录并尽早进行这些操作?或者……别的什么,也许?我不是先寻找最佳优化解决方案,而是先寻找改进,然后再看看是否可以迭代。 另外,关于您对最小数据百分比的估计,这里是an article which points to something else【参考方案2】:

因此,在仔细查看代码之后,根据查询的SELECT 部分中列出的列显示的数据,我观察到最后一个连接表没有带来任何贡献(不需要任何数据从它显示)到输出。

join acq_acct aa on aa.acq_code = r.acq_code
  and aa.acq_acct_code = r.acq_acct_code
  and aa.slc = 'MXM'

因此,我将此查询移至 EXISTS 子句并重新运行查询。我修改后的查询如下所示:

select atx.journal_id
    ,ab.c_date
from acct_batch ab 
    join acct_tx atx on ab.acct_id = atx.acct_id 
      and ab.batch_id = atx.batch_id
    join journal j on j.journal_id = atx.journal_id
      and j.journal_type_id = 6
    join acct a on a.acct_id = atx.acct_id 
      and a.acct_type_id = 32
    join payments p on p.payment_id = j.payment_id
    join routing r on r.route_id = p.route_id 
      and r.acq_code = 'RZ_NS'
where ab.c_date between to_date(to_char('01-JUL-2015')) and  last_day(sysdate)
    and exists (select 1
                from acq_acct aa
                where aa.acq_code = r.acq_code
                    and aa.acq_acct_code = r.acq_acct_code
                    and aa.slc = 'MXM');

这有助于将我的查询成本从 7388 提高到 292,这是一个巨大的差异。

希望我确实对此有正确的理解,并且我的解释也有道理。

如果有人认为我的结论不正确或“逻辑推理”不正确,请发表评论(目前,我上面的结论/解释对我来说很有意义)。

【讨论】:

它是否删除了 FTS?执行时间是否有所改善?老实说,这对我来说看起来很奇怪,因为 journal 和 acct_tx 是最大的表,除了它们之间的哈希连接之外,我没有看到任何其他选项。我会尝试在 journal(journal_id, journal_type_id) 上添加索引以绕过表访问。 @cristianv 这也是我的第一个想法,因为我认为索引会提高性能。但这是生产中的表,上面已经有很多其他索引,所以最后一个选择是添加另一个索引。

以上是关于Oracle:大幅提升查询性能的主要内容,如果未能解决你的问题,请参考以下文章

如何提高oracle的查询速度

云原生数据湖分析(DLA) 支持表格存储并发导出功能,性能大幅提升 !

HBase 1.3 发布,性能大幅提升

是否建议在 Oracle 中循环执行查询与一次性执行查询以获得显着的性能提升?

Oracle提升查询性能之-简单范围分区表的创建

oracle中decode函数如何提升查询语句性能的?