今天接到同事求助,说有一个select query,在Oracle上要跑一分多钟,他希望能在5s内出结果,该sql如下:

[sql]  view plain copy print ?
  1. Select  /*+ parallel(src, 8) */ distinct
  2. src.systemname as systemname
  3. ,  src.databasename as databasename
  4. ,  src.tablename as tablename
  5. ,  src.username as username
  6. from  <strong>meta_dbql_table_usage_exp_hst</strong> src
  7. inner <strong>join DR_QRY_LOG_EXP_HST</strong> rl on
  8. <strong>src.acctstringdate = rl.acctstringdate
  9. and src.queryid = rl.queryid</strong>
  10. And Src.Systemname = Rl.Systemname
  11. and src.acctstringdate > sysdate - 30
  12. And Rl.Acctstringdate > Sysdate - 30
  13. inner join  <strong>meta_dr_qry_log_tgt_all_hst </strong>tgt on
  14. upper(tgt.systemname) = upper('MOZART')
  15. And Upper(tgt.Databasename) = Upper('GDW_TABLES')
  16. And Upper(tgt.Tablename) = Upper('SSA_SLNG_LSTG_MTRC_SD')
  17. <strong>AND src.acctstringdate = tgt.acctstringdate
  18. and rl.statement_id = tgt.statement_id</strong>
  19. and rl.systemname = tgt.systemname
  20. And Tgt.Acctstringdate > Sysdate - 30
  21. And Not(
  22. Upper(Tgt.Systemname)=Upper(src.systemname)
  23. And
  24. Upper(Tgt.Databasename) = Upper(Src.Databasename)
  25. And
  26. Upper(Tgt.Tablename) = Upper(Src.Tablename)
  27. )
  28. And   tgt.Systemname is not null
  29. And   tgt.Databasename Is Not Null
  30. And   tgt.tablename is not null
  31. ;

SQL的简单分析

总得来看,这个SQL就是三个表(meta_dbql_table_usage_exp_hst,DR_QRY_LOG_EXP_HST,meta_dr_qry_log_tgt_all_hst)的INNER JOIN,这三个表数据量都在百万级别,且都是分区表(以acctstringdate为分区键),执行计划如下:
[sql]  view plain copy print ?
  1. ------------------------------------------------------------------------------------------------------------------------
  2. | Id  | Operation                              | Name                          | Rows  | Bytes | Cost  | Pstart| Pstop |
  3. ------------------------------------------------------------------------------------------------------------------------
  4. |   0 | SELECT STATEMENT                       |                               |     1 |   159 |  8654 |       |       |
  5. |   1 |  PX COORDINATOR                        |                               |       |       |       |       |       |
  6. |   2 |   PX SEND QC (RANDOM)                  | :TQ10002                      |     1 |   159 |  8654 |       |       |
  7. |   3 |    SORT UNIQUE                         |                               |     1 |   159 |  8654 |       |       |
  8. |   4 |     PX RECEIVE                         |                               |     1 |    36 |     3 |       |       |
  9. |   5 |      PX SEND HASH                      | :TQ10001                      |     1 |    36 |     3 |       |       |
  10. |*  6 |       TABLE ACCESS BY LOCAL INDEX ROWID| DR_QRY_LOG_EXP_HST            |     1 |    36 |     3 |       |       |
  11. |   7 |        NESTED LOOPS                    |                               |     1 |   159 |  8633 |       |       |
  12. |   8 |         NESTED LOOPS                   |                               |  8959 |  1076K|  4900 |       |       |
  13. |   9 |          BUFFER SORT                   |                               |       |       |       |       |       |
  14. |  10 |           PX RECEIVE                   |                               |       |       |       |       |       |
  15. |  11 |            PX SEND BROADCAST           | :TQ10000                      |       |       |       |       |       |
  16. |  12 |             PARTITION RANGE ITERATOR   |                               |     1 |    56 |  4746 |   KEY |    14 |
  17. |* 13 |              TABLE ACCESS FULL         | META_DR_QRY_LOG_TGT_ALL_HST   |     1 |    56 |  4746 |   KEY |    14 |
  18. |  14 |          PX BLOCK ITERATOR             |                               |  8959 |   586K|   154 |   KEY |   KEY |
  19. |* 15 |           TABLE ACCESS FULL            | META_DBQL_TABLE_USAGE_EXP_HST |  8959 |   586K|   154 |   KEY |   KEY |
  20. |  16 |         PARTITION RANGE ITERATOR       |                               |     1 |       |     2 |   KEY |   KEY |
  21. |* 17 |          INDEX RANGE SCAN              | DR_QRY_LOG_EXP_HST_IDX        |     1 |       |     2 |   KEY |   KEY |
  22. ------------------------------------------------------------------------------------------------------------------------
  23. Predicate Information (identified by operation id):
  24. ---------------------------------------------------
  25. 6 - filter("RL"."STATEMENT_ID"="TGT"."STATEMENT_ID" AND "RL"."SYSTEMNAME"="TGT"."SYSTEMNAME" AND "SRC"."SYSTEMNAME"="RL"."SYSTEMNAME")
  26. 13 - filter(UPPER("TGT"."SYSTEMNAME")='MOZART' AND UPPER("TGT"."DATABASENAME")='GDW_TABLES' AND
  27. UPPER("TGT"."TABLENAME")='SSA_SLNG_LSTG_MTRC_SD' AND "TGT"."ACCTSTRINGDATE">SYSDATE@!-30 AND "TGT"."SYSTEMNAME" IS NOT NULL
  28. "TGT"."DATABASENAME" IS NOT NULL AND "TGT"."TABLENAME" IS NOT NULL)
  29. 15 - filter("SRC"."ACCTSTRINGDATE"="TGT"."ACCTSTRINGDATE" AND (UPPER("TGT"."SYSTEMNAME")<>UPPER("SRC"."SYSTEMNAME") OR
  30. UPPER("TGT"."DATABASENAME")<>UPPER("SRC"."DATABASENAME") OR UPPER("TGT"."TABLENAME")<>UPPER("SRC"."TABLENAME")) AND
  31. "SRC"."ACCTSTRINGDATE">SYSDATE@!-30)
  32. 17 - access("SRC"."QUERYID"="RL"."QUERYID" AND "SRC"."ACCTSTRINGDATE"="RL"."ACCTSTRINGDATE")
  33. filter("RL"."ACCTSTRINGDATE">SYSDATE@!-30)

定位问题

从上面执行计划中的表连接方式可以知道,这三个表之间进行了两次NESTED LOOP,问题出现在最里层的NESTED LOOP(对两个表都做了TABLE FULL SCAN),因为表都是百万级别的(即时过滤后的数据量也不小),性能问题就出现在内表(即被驱动表)META_DBQL_TABLE_USAGE_EXP_HST做了太多次的全表扫描。如果能把全表扫描转换成索引,则性能可以大幅度提高。
下面是NESTED LOOP的介绍:
嵌套连接把要处理的数据集分为外部循环(驱动数据源)和内部循环(被驱动数据源),外部循环只执行一次,内部循环执行的次数等于外部循环执行返回的数据个数。
这种连接的好处是内存使用非常少。
如果驱动数据源有限,且被驱动表在连接列上有相应的索引,则这种连接方式才是高效的。
下面是这三个表上索引的情况:
[sql]  view plain copy print ?
  1. SQL> select index_name, table_name from user_indexes where table_name in ('DR_QRY_LOG_EXP_HST',upper('meta_dbql_table_usage_exp_hst'), upper('meta_dr_qry_log_tgt_all_hs
  2. INDEX_NAME                                                   TABLE_NAME
  3. ------------------------------------------------------------ ------------------------------------------------------------
  4. META_DR_QRY_LOG_TGT_ALL_IDX                                  META_DR_QRY_LOG_TGT_ALL_HST
  5. META_DBQL_TUSAGE_EHST_IDX                                    META_DBQL_TABLE_USAGE_EXP_HST
  6. DR_QRY_LOG_EXP_HST_IDX                                       DR_QRY_LOG_EXP_HST
  7. CREATE INDEX "GV"."META_DR_QRY_LOG_TGT_ALL_IDX" ON "GV"."META_DR_QRY_LOG_TGT_ALL_HST" ("STATEMENT_ID", "ACCTSTRINGDATE")
  8. CREATE INDEX "GV"."META_DBQL_TUSAGE_EHST_IDX" ON "GV"."META_DBQL_TABLE_USAGE_EXP_HST" ("QUERYID", "ACCTSTRINGDATE")
  9. CREATE INDEX "GV"."DR_QRY_LOG_EXP_HST_IDX" ON "GV"."DR_QRY_LOG_EXP_HST" ("QUERYID", "ACCTSTRINGDATE")
这三个索引都是本地分区索引(都包含分区键acctstringdate),很显然,DR_QRY_LOG_EXP_HST表少了个索引,因为它与表meta_dr_qry_log_tgt_all_hst 在statement_id上做join,因此应该在它的statement_id上也创建本地分区索引如下:
[sql]  view plain copy print ?
  1. create index DR_QRY_LOG_EXP_HST_IDX2 on gv.DR_QRY_LOG_EXP_HST (statement_id,ACCTSTRINGDATE) local;

性能对比

新的执行计划如下:
[sql]  view plain copy print ?
  1. ------------------------------------------------------------------------------------------------------------------------
  2. | Id  | Operation                              | Name                          | Rows  | Bytes | Cost  | Pstart| Pstop |
  3. ------------------------------------------------------------------------------------------------------------------------
  4. |   0 | SELECT STATEMENT                       |                               |     1 |   159 |  4838 |       |       |
  5. |   1 |  SORT UNIQUE                           |                               |     1 |   159 |  4838 |       |       |
  6. |*  2 |   TABLE ACCESS BY LOCAL INDEX ROWID    | META_DBQL_TABLE_USAGE_EXP_HST |     1 |    67 |     3 |       |       |
  7. |   3 |    NESTED LOOPS                        |                               |     1 |   159 |  4816 |       |       |
  8. |   4 |     NESTED LOOPS                       |                               |    18 |  1656 |  4762 |       |       |
  9. |   5 |      PARTITION RANGE ITERATOR          |                               |     1 |    56 |  4746 |   KEY |    14 |
  10. |*  6 |       TABLE ACCESS FULL                | META_DR_QRY_LOG_TGT_ALL_HST   |     1 |    56 |  4746 |   KEY |    14 |
  11. |   7 |      PARTITION RANGE ITERATOR          |                               |    18 |   648 |    16 |   KEY |    14 |
  12. |*  8 |       TABLE ACCESS BY LOCAL INDEX ROWID| DR_QRY_LOG_EXP_HST            |    18 |   648 |    16 |   KEY |    14 |
  13. |*  9 |        <strong>INDEX RANGE SCAN                | DR_QRY_LOG_EXP_HST_IDX2</strong>       |    31 |       |    15 |   KEY |    14 |
  14. |  10 |     PARTITION RANGE ITERATOR           |                               |     1 |       |     2 |   KEY |   KEY |
  15. |* 11 |      INDEX RANGE SCAN                  | META_DBQL_TUSAGE_EHST_IDX     |     1 |       |     2 |   KEY |   KEY |
  16. ------------------------------------------------------------------------------------------------------------------------
  17. Predicate Information (identified by operation id):
  18. ---------------------------------------------------
  19. 2 - filter((UPPER("TGT"."SYSTEMNAME")<>UPPER("SRC"."SYSTEMNAME") OR
  20. UPPER("TGT"."DATABASENAME")<>UPPER("SRC"."DATABASENAME") OR UPPER("TGT"."TABLENAME")<>UPPER("SRC"."TABLENAME"))
  21. AND "SRC"."SYSTEMNAME"="RL"."SYSTEMNAME")
  22. 6 - filter(UPPER("TGT"."SYSTEMNAME")='MOZART' AND UPPER("TGT"."DATABASENAME")='GDW_TABLES' AND
  23. UPPER("TGT"."TABLENAME")='SSA_SLNG_LSTG_MTRC_SD' AND "TGT"."ACCTSTRINGDATE">SYSDATE@!-30 AND "TGT"."SYSTEMNAME"
  24. IS NOT NULL AND "TGT"."DATABASENAME" IS NOT NULL AND "TGT"."TABLENAME" IS NOT NULL)
  25. 8 - filter("RL"."SYSTEMNAME"="TGT"."SYSTEMNAME")
  26. 9 - access("RL"."STATEMENT_ID"="TGT"."STATEMENT_ID" AND "RL"."ACCTSTRINGDATE">SYSDATE@!-30 AND
  27. "RL"."ACCTSTRINGDATE" IS NOT NULL)
  28. 11 - access("SRC"."QUERYID"="RL"."QUERYID" AND "SRC"."ACCTSTRINGDATE"="RL"."ACCTSTRINGDATE")
  29. filter("SRC"."ACCTSTRINGDATE"="TGT"."ACCTSTRINGDATE" AND "SRC"."ACCTSTRINGDATE">SYSDATE@!-30)

从新的的执行计划可以看出,它的第一个NESTED LOOP果然用了最新创建的索引。

下面是执行时间:
[plain]  view plain copy print ?
  1. 已用时间:  00: 00: 02.16
两秒种搞定,远远超出他期望的5s :)

方法总结

NESTED LOOP高效的条件: 驱动数据源有限,且被驱动表在连接列上有相应的索引。

一个通过添加本地分区索引提高SQL性能的案例相关推荐

  1. oracle全局索引改成本地索引,解析一个通过添加本地分区索引提高SQL性能的案例...

    该sql如下: Select  /*+ parallel(src, 8) */ distinct src.systemname as systemname ,  src.databasename as ...

  2. [Oracle] 一个通过添加本地分区索引提高SQL性能的案例

    今天接到同事求助,说有一个select query,在Oracle上要跑一分多钟,他希望能在5s内出结果,该sql如下: Select /*+ parallel(src, 8) */ distinct ...

  3. 建立合理的索引提高SQL Server的性能

    建立合理的索引提高SQL Server的性能- 标签:索引,性能优化 建立合理的索引提高SQL Server的性能 在应用系统中,尤其在联机事务处理系统中,对数据查询及处理速度已成为衡量应用系统成败的 ...

  4. 【Oracle】表级别分区操作对索引(本地分区索引,全局分区索引,非分区索引)的影响

    --参考自<Oracle索引技术> 先贴上结论吧: 下面对以上几种操作分别测试: 创建测试表及索引 --创建测试分区表 CREATE TABLE employees_parttest (e ...

  5. oracle全局索引改成本地索引,FAQ : 如何获得 Oracle 分区索引的类型 - 全局分区索引、本地分区索引...

    Oracle 数据库针对分区的信息,通过多个数据字典视图来维护,所以在获取信息时,经常会困惑 DBA 们. 例如,如何获取 分区索引的类型,如何判断一个索引,是 全局分区索引,还是本地分区索引? 通过 ...

  6. dbcc dbreindex server sql_DBCC DBREINDEX重建索引提高SQL Server性能

    DBCC DBREINDEX重建索引提高SQL Server性能 [转载]大多数SQL Server表需要索引来提高数据的访问速度,如果没有索引,SQL Server 要进行表格扫描读取表中的每一个记 ...

  7. 转 五种提高 SQL 性能的方法

    提高 SQL 性能的方法 有时, 为了让应用程序运行得更快,所做的全部工作就是在这里或那里做一些很小调整.啊,但关键在于确定如何进行调整!迟早您会遇到这种情况:应用程序中的 SQL 查询不能按照您想要 ...

  8. 提高sql性能的方法_三种提高T-SQL性能的方法

    提高sql性能的方法 介绍 (Introduction) When customers used to ask for advice to solve some T-SQL Problem, they ...

  9. dbcc dbreindex server sql_DBCC DBREINDEX重建索引提高SQL Server性能

    大多数SQL Server表需要索引来提高数据的访问速度,如果没有索引,SQL Server 要进行表格扫描读取表中的每一个记录才能找到索要的数据.索引可以分为簇索引和非簇索引,簇索引通过重排表中的数 ...

最新文章

  1. linux 树莓派查看ip,树莓派 常用Linux命令
  2. 【C#食谱】【杭帮菜】菜单2:写一个TCP客户端
  3. 简单工厂模式、工厂方法模式、抽象工厂模式 之间的对比
  4. 【Spring实战】注入非Spring Bean对象
  5. Acwing第 31 场周赛【完结】
  6. HDR和SDR的区别
  7. java.lang.IllegalStateException: Not connected to server
  8. php设置session 生命周期,设置session的生命周期(php)
  9. Notepad++便签模式
  10. 可怕!CPU 竟成了黑客的帮凶!
  11. 蔡工RK系列Android驱动开发入门视频课程
  12. php中几个操作函数参数的函数func_num_args() func_get_args() func_get_arg($i)php
  13. 下列关于linux扩展名说法错误的是,全国计算机一级考试选择题集锦(2015年1月)
  14. Sybase数据库整体迁移纪要
  15. 偷梁换柱“Windows 11安装包”竟成了恶意程序?
  16. 单侧上行速度测试软件,20210708-确认 低开单边上行,箱体理论几乎是贯穿a股运行周期的,如果平时容易过分看好行情,或者过分看弱行情,不妨就以最中庸的箱体去验证。... - 雪球...
  17. Java之BIO网络编程
  18. mysql表中的ak什么意思_数据库 ak pk
  19. android打败苹果,苹果iOS打败安卓的另一面:配件多于Android
  20. YOLOv5剪枝✂️| 模型剪枝实战篇

热门文章

  1. Jetson nano (4GB B01) 系统安装,官方Demo测试 (目标检测、手势识别)
  2. sqlserver转mysql_数据库 SQLServer转MySQL数据库
  3. 已删除的回收站文件恢复方法
  4. 如何解决C++编译错误C2280尝试引用已删除的函数【每天一个小技巧】
  5. java通过poi导出excel和pdf
  6. R语言绘制复杂抽样设计数据cox回归生存曲线(Kaplan-Meier)
  7. 微波电路中的线性和非线性
  8. 干货 | 敏捷培训必备小游戏,都在这里了!
  9. Udesk即时通讯网页插件发送咨询对象(一、使用内嵌代码)
  10. Jboot 跨域请求