【问题标题】:Can this query be made faster?这个查询可以更快吗?
【发布时间】:2019-07-27 00:47:24
【问题描述】:

我的 Oracle 查询占用了 1.5 分钟,我不知道是因为查询编写效率低下、索引选择不当还是其他一些我无法控制的数据库问题。

为了保护 IP,更改了一些表格和数据。

SELECT /*+ PARALLEL (AUTO) */ COUNT(DISTINCT SUD_USERID)
FROM (
    SELECT /*+ PARALLEL (AUTO) */
           SUD_USERID ,
           CASE WHEN SCH_PAGETYPE = 'Page' AND SUD_EVENTTYPE = 'S'
               THEN 'EVENTTYPE1'
                WHEN SCH_PAGETYPE = 'Page' AND SUD_EVENTTYPE = 'V'
             THEN 'EVENTTYPE2'
             WHEN SCH_PAGETYPE = 'Hub' AND SUD_EVENTTYPE = 'S'
             THEN 'EVENTTYPE3'
        END AS CALC_EVENT_SOURCE,
        SUD_EVENT_SOURCE
        FROM
       (
            SELECT /*+ PARALLEL (AUTO) */
                     UPPER(PAGETYPE)|| '-' || SCH.ID PAGETYPE_ID ,
                SCH.PAGETYPE SCH_PAGETYPE
            FROM TABLE1 SCH
            WHERE   SCH.PAGETYPE IN ('Page', 'Hub')
                AND SCH.CATEGORY_NAME NOT IN ('archive', 'testcategory')
        )
        INNER JOIN (
            SELECT /*+ PARALLEL (AUTO) */
                DISTINCT SUD.TRACEID TRACEID ,
                SUD.EVENTTYPE SUD_EVENTTYPE ,
                SUD.USERID SUD_USERID,
                SUD.EVENT_SOURCE SUD_EVENT_SOURCE
            FROM
                SOMESCHEMA.USAGE_DETAILS SUD
            WHERE
                SUD.EVENTTYPE IN ('S', 'V')
        )
       ON TRACEID = PAGETYPE_ID
        INNER JOIN USER_JOB_FAMILY_MAPPING SFD
       ON SUD_USERID = SFD.USERID
       )
    WHERE CALC_EVENT_SOURCE = SUD_EVENT_SOURCE

我无法复制解释计划的文本(通过 DBeaver 生成) 但这里是截图:

USAGE_DETAILS table has 3941810 rows
TABLE1 has 5908 rows
USER_JOB_FAMILY_MAPPING has 578233 rows

这些表中没有任何键。 USAGE_DETAILS.TRACEID 是 VARCHAR2(500) NOT NULL 具有函数 index=SUBSTR("TRACEID",1,4) 和 另一个声明为默认但在该列上的索引。

USAGE_DETAILS.USERID is VARCHAR2(50) NOT NULL
USAGE_DETAILS.EVENTTYPE is VARCHAR2(10) NOT NULL and has default index
USAGE_DETAILS.EVENT_SOURCE is VARCHAR2(200) NOT NULL and has default index

我尝试对完整表而不是括号内生成的(子选择?)表进行内部连接,但这并没有更好地执行,并且还限制了我在 WHERE 子句中使用别名的能力。

我不知道这是在哪种机器上运行,只是它是为开发而设置的。我希望这个查询能在 10 秒内给我准确的答案。有时上面的查询即使在 10 多分钟后仍然没有返回。

Plan hash value: 2784166315

-----------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                    | Name                       | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                             |                            |     1 |    27 |       |   258K  (1)| 00:00:11 |
|   1 |  SORT AGGREGATE                              |                            |     1 |    27 |       |            |          |
|   2 |   VIEW                                       | VM_NWVW_1                  |  1809K|    46M|       |   258K  (1)| 00:00:11 |
|   3 |    HASH GROUP BY                             |                            |  1809K|   745M|       |   258K  (1)| 00:00:11 |
|*  4 |     HASH JOIN                                |                            |  1809K|   745M|       |   258K  (1)| 00:00:11 |
|*  5 |      TABLE ACCESS FULL                       | TABLE1S                    |  5875 |   172K|       |   309   (0)| 00:00:01 |
|   6 |      MERGE JOIN SEMI                         |                            |  3079K|  1180M|       |   257K  (1)| 00:00:11 |
|   7 |       SORT JOIN                              |                            |  3079K|  1139M|       |   254K  (1)| 00:00:10 |
|   8 |        VIEW                                  |                            |  3079K|  1139M|       |   254K  (1)| 00:00:10 |
|   9 |         HASH UNIQUE                          |                            |  3079K|  1139M|  1202M|   254K  (1)| 00:00:10 |
|  10 |          INLIST ITERATOR                     |                            |       |       |       |            |          |
|  11 |           TABLE ACCESS BY INDEX ROWID BATCHED| USAGE_DETAILS              |  3079K|  1139M|       |    46   (0)| 00:00:01 |
|* 12 |            INDEX RANGE SCAN                  | IDX_UUD_EVENTTYPE          | 13704 |       |       |    46   (0)| 00:00:01 |
|* 13 |       SORT UNIQUE                            |                            |   578K|  7905K|    22M|  3558   (1)| 00:00:01 |
|  14 |        INDEX FAST FULL SCAN                  | USERID_IDX                 |   578K|  7905K|       |   704   (1)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("TRACEID"=UPPER("PAGETYPE")||'-'||TO_CHAR("SCH"."ID"))
       filter("from$_subquery$_004"."SUD_EVENT_SOURCE"=CASE  WHEN (("SCH"."PAGETYPE"='Page') AND
              ("from$_subquery$_004"."SUD_EVENTTYPE"='S')) THEN 'EVENTTYPE1' WHEN (("SCH"."PAGETYPE"='Page') AND
              ("from$_subquery$_004"."SUD_EVENTTYPE"='V')) THEN 'EVENTTYPE2' WHEN (("SCH"."PAGETYPE"='Hub') AND
              ("from$_subquery$_004"."SUD_EVENTTYPE"='S')) THEN 'EVENTTYPE3' END )
   5 - filter("SCH"."CATEGORY_NAME"<>'archive' AND "SCH"."CATEGORY_NAME"<>'testcategory' AND ("SCH"."PAGETYPE"='Hub' OR
              "SCH"."PAGETYPE"='Page'))
  12 - access("SUD"."EVENTTYPE"='S' OR "SUD"."EVENTTYPE"='V')
  13 - access("from$_subquery$_004"."SUD_USERID"="SFD"."USERID")
       filter("from$_subquery$_004"."SUD_USERID"="SFD"."USERID")

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
   - automatic DOP: Computed Degree of Parallelism is 1 because of no expensive parallel operation

运行后 exec dbms_stats.gather_table_stats(ownname=>'SCHEMA1',tabname=>'USAGE_DETAILS'); exec dbms_stats.gather_table_stats(ownname=>'SCHEMA1',tabname=>'TABLE1');

我有这个新计划:

SQL> select plan_table_output from table(dbms_xplan.display());
Plan hash value: 3419946982                                                                                                         
                                                                                                                                    
----------------------------------------------------------------------------------------------------------------                    
| Id  | Operation                 | Name                       | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |                    
----------------------------------------------------------------------------------------------------------------                    
|   0 | SELECT STATEMENT          |                            |     1 |    27 |       | 70152   (1)| 00:00:03 |                    
|   1 |  SORT AGGREGATE           |                            |     1 |    27 |       |            |          |                    
|   2 |   VIEW                    | VM_NWVW_1                  | 53144 |  1401K|       | 70152   (1)| 00:00:03 |                    
|   3 |    HASH GROUP BY          |                            | 53144 |    21M|    21M| 70152   (1)| 00:00:03 |                    
|*  4 |     HASH JOIN RIGHT SEMI  |                            | 53144 |    21M|    14M| 65453   (1)| 00:00:03 |                    
|   5 |      INDEX FAST FULL SCAN | USERID_IDX                 |   578K|  7905K|       |   704   (1)| 00:00:01 |                    
|*  6 |      HASH JOIN            |                            | 53144 |    20M|       | 62995   (1)| 00:00:03 |                    
|   7 |       JOIN FILTER CREATE  | :BF0000                    |  5503 |   161K|       |   309   (0)| 00:00:01 |                    
|*  8 |        TABLE ACCESS FULL  | TABLE1                     |  5503 |   161K|       |   309   (0)| 00:00:01 |                    
|   9 |       VIEW                |                            |  3549K|  1259M|       | 62677   (1)| 00:00:03 |                    
|  10 |        HASH UNIQUE        |                            |  3549K|   159M|   203M| 62677   (1)| 00:00:03 |                    
|  11 |         JOIN FILTER USE   | :BF0000                    |  3549K|   159M|       | 21035   (1)| 00:00:01 |                    
|* 12 |          TABLE ACCESS FULL| USAGE_DETAILS              |  3549K|   159M|       | 21035   (1)| 00:00:01 |                    
----------------------------------------------------------------------------------------------------------------                    
                                                                                                                                    
Predicate Information (identified by operation id):                                                                                 
---------------------------------------------------                                                                                 
                                                                                                                                    
   4 - access("from$_subquery$_004"."SUD_USERID"="SFD"."USERID")                                                                    
   6 - access("TRACEID"=UPPER("PAGETYPE")||'-'||TO_CHAR("SCH"."ID"))                                                        
       filter("from$_subquery$_004"."SUD_EVENT_SOURCE"=CASE  WHEN (("SCH"."PAGETYPE"='Page') AND                                    
              ("from$_subquery$_004"."SUD_EVENTTYPE"='S')) THEN 'EVENTTYPE1' WHEN (("SCH"."PAGETYPE"='Page') AND                     
              ("from$_subquery$_004"."SUD_EVENTTYPE"='V')) THEN 'EVENTTYPE2' WHEN (("SCH"."PAGETYPE"='Hub') AND                            
              ("from$_subquery$_004"."SUD_EVENTTYPE"='S')) THEN 'EVENTTYPE3' END )                                                   
   8 - filter("SCH"."CATEGORY_NAME"<>'archive' AND "SCH"."CATEGORY_NAME"<>'testcategory' AND                                        
              ("SCH"."PAGETYPE"='Hub' OR "SCH"."PAGETYPE"='Page'))                                                                  
  12 - filter(("SUD"."EVENTTYPE"='S' OR "SUD"."EVENTTYPE"='V') AND                                                                  
              SYS_OP_BLOOM_FILTER(:BF0000,"SUD"."TRACEID"))                                                                 
                                                                                                                                    
Note                                                                                                                                
-----                                                                                                                               
   - automatic DOP: Computed Degree of Parallelism is 1 because of no expensive parallel operation                                  

37 rows selected.

更可能是计算统计信息极大地帮助了这个查询,还是有人做了我不知道的其他事情?是的,查询运行得更好,但如果我知道原因,我也会感觉更好。

【问题讨论】:

  • 抱歉,我在粘贴初始询问时遇到问题,必须保存多次才能查看 stackoverflow 抱怨的内容。
  • 我建议您使用explain plan for SELECT ...; 生成执行计划,然后使用select * from table(dbms_xplan.display);。一般来说,您应该避免使用所有 IDE 执行计划,它们总是会遗漏信息。在这种情况下,我很想看到“注意”部分。它可能包括一些关于并行性的信息。并行性通常有助于长时间运行的查询。如果您希望不到 10 秒,您可能不想使用并行性。
  • 如果您想进行更多调查,您可能需要使用 SQL 监控工具。在GV$SQL中找到相关的SQL_ID,然后运行select dbms_sqltune.report_sql_monitor('your SQL_ID') from dual;,结果会显示估计值和实际值。有了这些信息,我们或许能够判断 Oracle 的估计在哪里出错了。
  • 我在SqlPlus下添加了select的explain plan,spooled结果添加到日志中。
  • USAGE_DETAILSEVENTTYPE IN ('S', 'V') 中有多少行? Oracle 认为只有13704 这样的行,我想会有更多。如果是这样检查您的表格统计数据,如果事件类型分布偏斜,则使用直方图

标签: sql oracle performance query-optimization


【解决方案1】:

喂,

查看您的 SQL 后,我注意到您的语句充满了字符串比较和搜索。例如

        SELECT /*+ PARALLEL (AUTO) */
                 UPPER(PAGETYPE)|| '-' || SCH.ID PAGETYPE_ID ,
            SCH.PAGETYPE SCH_PAGETYPE
        FROM TABLE1 SCH
        WHERE   SCH.PAGETYPE IN ('Page', 'Hub')
            AND SCH.CATEGORY_NAME NOT IN ('archive', 'testcategory')

这可以通过 2 种方式进行索引。 首先:创建具有“Page”、“Hub”和您需要的其他类型的表,为列索引创建然后“替换”基本上调整您的查询以解析这些索引而不是字符串比较。 表可以在列上有多个索引,这些索引必须小心处理,因为它们会在数据库大小方面产生问题。 此外,我会检查最大的表是否是最大的,并将它们的选择重新排序到最后。含义:

如果一个表有 12 行,另一个有 100 行。先放 12 行表,然后放 100 行。由于表格和嵌套和链接,这将在您的情况下成倍增加。

我又做了 1 次审查,发现自己疏忽了。

USAGE_DETAILS table has 3941810 rows
TABLE1 has 5908 rows
USER_JOB_FAMILY_MAPPING has 578233 rows

首先过滤表1,这已经很昂贵了,然后Inner join raw USAGE_DETAILS 然后选择join ID-s。 然后内连接USER_JOB_FAMILY_MAPPING,然后选择。原因是连接是在可能是 int 类型的 ID 上完成的。

【讨论】:

  • 我非常感谢您所说的,并愿意尝试这样做。但是,其中一些字段已经具有由其他人定义的索引。我可以有多个索引吗?此外,我认为未过滤的表大小在注释中,但按行数计算,SCH 应该远小于 SUD。如果我理解你的话,它在 SQL 中看到的第一个表应该小于连接中的第二个表。
  • 好的,明白你的意思,我会调整我的答案来解决你的问题。
【解决方案2】:

像这样收集相关对象的统计数据:

begin
    dbms_stats.gather_table_stats(ownname => user,         tabname => 'TABLE1');
    dbms_stats.gather_table_stats(ownname => 'SOMESCHEMA', tabname => 'USAGE_DETAILS');
end;
/

执行计划中的这一行意味着其中一个表缺少统计信息:

- dynamic statistics used: dynamic sampling (level=2)

并非所有使用动态抽样都意味着缺少统计信息,但级别 2 非常可疑。该采样级别通常用于"Apply dynamic sampling to all unanalyzed tables."

优化器统计信息对于 Oracle 制定良好的执行计划是必要的。连接少量数据的算法和访问路径不同于连接大量数据的算法和访问路径。优化器统计信息有助于 Oracle 估计结果的大小并制定好的计划。

如果这解决了您的问题,您还应该调查根本原因。优化器统计数据应该总是在大的变化之后手动收集,并且每晚由系统自动收集。如果您有一个大型 ETL 过程会显着更改表,则它应该在最后包含对 DBMS_STATS 的调用。默认情况下,数据库在每晚 10 点收集统计信息,除非 DBA 愚蠢地禁用了自动任务。

如果这不能解决问题,则使用DBMS_SQLTUNEGATHER_PLAN_STATISTICS_HINT 重新生成具有实际数字的执行计划。 SQL 调优是关于优化操作的。你的 SQL 语句有 14 个操作,每个操作都像一个微型程序。我们需要知道是哪一项操作导致了问题。查找实际基数和实际运行时间,并将它们与估计值进行比较,对诊断 SQL 问题有很大帮助。


我们如何知道收集统计数据是导致性能问题的原因?

我们不能 100% 确定。但可以肯定的是,收集统计数据是改进的原因,原因有几个。

错误或缺失的统计信息占所有 Oracle 性能问题的很大一部分。问任何 DBA,他们都会有很多关于缺失统计数据的故事。

Note 部分的变化强烈暗示幕后没有其他奇怪的事情发生。有很多技巧可以静默地修复查询,例如 SQL 配置文件、基线、自适应重新优化、动态采样(出现在第一个计划中,但没有出现在第二个计划中,因为统计数据更好)等等。但是如果使用了这些技巧,它们会出现在Note 部分。

【讨论】:

  • 我在两张表上运行了收集统计信息,然后走开了,因为这需要很长时间。第二天,查询运行了大约 3 秒。这个数据库由我公司的其他部分控制,我不知道他们是否对它做了任何事情。但是,我添加了一个新的解释计划。
  • 非常感谢您。我确实需要添加 3 个其他 SQL 查询,所以我的时间回到了大约 12-14 秒,但我认为这是另一个问题。不幸的是,他们打的是同一张表,所以我猜计算统计数据不会解决这个问题。
  • 对@JonHeller 上述评论的一个更正。使用动态抽样并不意味着表格缺少统计信息。
  • @BobC 谢谢,我调整了答案以解释只有第 2 级可疑。
猜你喜欢
  • 2016-04-23
  • 1970-01-01
  • 2013-06-23
  • 1970-01-01
  • 2013-02-10
  • 2011-09-05
  • 2019-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多