暂无图片
暂无图片
9
暂无图片
暂无图片
暂无图片

关于sql优化cardinality的hint的应用一例

原创 杜伟 2023-07-19
1181

关于cardinality的hint的说明:

CARDINALITY是针对单个目标表的Hint,它可以用来设置对目标表执行扫描操作后返回结果集的Cardinality的值,它所能够设置的扫描类型包括对目标表的全表扫描、索引范围扫描、索引全扫描和索引快速全扫描。

/*+ cardinality(t 1000) */           ---针对t表设置其结果集是1000,在数据库优化器进行执行计划预估时,其结果集是10000。

示例如下:

select /*+ cardinality(emp 1000) */ * from emp;
Plan Hash Value : 3956160932 -------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | Time | -------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1000 | 42000 | 3 | 00:00:01 | | 1 | TABLE ACCESS FULL | EMP | 1000 | 42000 | 3 | 00:00:01


生产11.2.0.4上有个不怎么复杂的sql其执行时长近2000s,其语句如下:

select   * from dw_20230718 t where CREATED like '20220301%' AND object_TYPE ='TABLE'
and object_name not in 
(select object_name from dw_20230718 group by object_name,status,owner having count(1)>32 )
----------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | Time | ----------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 111 | 11701 | 00:02:21 | | * 1 | FILTER | | | | | | | * 2 | TABLE ACCESS BY INDEX ROWID | DW_20230718 | 1 | 111 | 5 | 00:00:01 | | * 3 | INDEX RANGE SCAN | IDX_0718 | 1 | | 3 | 00:00:01 | | * 4 | FILTER | | | | | | | 5 | HASH GROUP BY | | 24 | 912 | 11696 | 00:02:21 | | 6 | TABLE ACCESS FULL | DW_20230718 | 2761440 | 104934720 | 11619 | 00:02:20 | -----------------------------------------------------------------------------------------------

该执行计划在最消耗性能的就是子查询执行多次,需要消除第四步的FILTER让执行计划走上hash join anti;

dw_20230718表是270w行的一个表,经过基于dba_objects多次插入,created是修改为varchar2(14)的存储时间数字到秒如 20230718120132;

CREATED like '20220301%'真实记录有1300多条,对应索引的信息如下:


可以肯定,CREATED like '20220301%'在预估行的时候,优化器预估错了,这种like不好正确预估真实的行

尝试收集字段的直方图还是不行,索引也是会自行收集直方图的。

EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=>'SCOTT',TABNAME=>'DW_20230718',METHOD_OPT=>'FOR COLUMNS SIZE AUTO CREATED',no_invalidate => FALSE);

尝试hint use_hash不行,因为子查询是经过分组查询的,相当于一个视图了。想起来之前看过一个cardinality的hint,今天就来试试吧!

select /*+ cardinality(t 1000) */  * from dw_20230718 t where CREATED like '20220301%' AND object_TYPE ='TABLE'
and object_name not in 
(select object_name from dw_20230718 group by object_name,status,owner having count(1)>32 );-----------------------------------------------------------------------------------------------
| Id  | Operation                      | Name        | Rows    | Bytes     | Cost  | Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |             |      10 |      1770 | 21611 | 00:04:20 |
| * 1 |   HASH JOIN ANTI NA            |             |      10 |      1770 | 21611 | 00:04:20 |
| * 2 |    TABLE ACCESS BY INDEX ROWID | DW_20230718 |    1000 |    111000 |     5 | 00:00:01 |
| * 3 |     INDEX RANGE SCAN           | IDX_0718    |       1 |           |     3 | 00:00:01 |
|   4 |    VIEW                        | VW_NSO_1    |   59515 |   3927990 | 21606 | 00:04:20 |
| * 5 |     FILTER                     |             |         |           |       |          |
|   6 |      HASH GROUP BY             |             |   59515 |   2261570 | 21606 | 00:04:20 |
|   7 |       TABLE ACCESS FULL        | DW_20230718 | 2761440 | 104934720 | 11619 | 00:02:20 |
-----------------------------------------------------------------------------------------------

执行计划走上了HASH JOIN ANTI NA,子查询结果当做一个视图作为hash join的被驱动表,观察第三步预估结果是1行,第二步的预估结果因为有hint是1000,让优化器走上了正确的执行计划了

生产上原来2000s的语句,hint后只需要5s不到。

总结:

1、关于like时,不管有没有使用到索引,此时优化器预估的行,不精确,需要多关注下。like '%xxx%'固定选择率为5%;

2、关于语句有子查询时,需要多关注下,看是否可以改写

3、在执行计划中遇到FILTER表关联方式时,很多时候都会遇到坑,需要多关注下,是否可以改写等。



「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论