首页 > 数据库 >Oracle "脑残" CBO 优化案例

Oracle "脑残" CBO 优化案例

时间:2024-04-24 23:33:41浏览次数:24  
标签:00 01 NAME CBO HHHHHH Oracle 脑残 ID SELECT

今天晚上下班回来才有空看群,群友发了一条很简单的慢SQL问怎么优化。

非常简单,我自己模拟的数据。

表结构:

-- auto-generated definition
CREATE TABLE HHHHHH
(
    ID           NUMBER NOT NULL
        PRIMARY KEY,
    NAME         VARCHAR2(20),
    PARAGRAPH_ID NUMBER
)
/

CREATE INDEX IDX_1_2_PARAGRAPH_HIST_RULE
    ON HHHHHH (PARAGRAPH_ID)
/

CREATE INDEX IDX_1_2_NAME_HIST_RULE
    ON HHHHHH (NAME)
/

数据量:

SQL> select count(1) from HHHHHH;

  COUNT(1)
----------
    200002

Elapsed: 00:00:00.00

慢SQL:

SELECT a.* FROM hhhhhh a
WHERE   a.name IN (
    SELECT   name from hhhhhh b
    GROUP BY b.name HAVING count(DISTINCT b.paragraph_id) = 1
);



Plan hash value: 1063187735
 
------------------------------------------------------------------------------------
| Id  | Operation              | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |           |     1 |    38 |     5  (20)| 00:00:01 |
|*  1 |  FILTER                |           |       |       |            |          |
|   2 |   TABLE ACCESS FULL    | HHHHHH    |     1 |    38 |     2   (0)| 00:00:01 |
|*  3 |   FILTER               |           |       |       |            |          |
|   4 |    HASH GROUP BY       |           |     1 |    25 |     3  (34)| 00:00:01 |
|   5 |     VIEW               | VM_NWVW_1 |     1 |    25 |     3  (34)| 00:00:01 |
|   6 |      SORT GROUP BY     |           |     1 |    25 |     3  (34)| 00:00:01 |
|   7 |       TABLE ACCESS FULL| HHHHHH    |     1 |    25 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
"   1 - filter( EXISTS (SELECT 0 FROM  (SELECT ""B"".""PARAGRAPH_ID"" "
"              ""$vm_col_1"",""B"".""NAME"" ""$vm_col_2"" FROM ""HHHHHH"" ""B"" GROUP BY "
"              ""B"".""NAME"",""B"".""PARAGRAPH_ID"") ""VM_NWVW_1"" GROUP BY ""$vm_col_2"" HAVING "
"              ""$vm_col_2""=:B1 AND COUNT(""$vm_col_1"")=1))"
"   3 - filter(""$vm_col_2""=:B1 AND COUNT(""$vm_col_1"")=1)"

 跑了24秒没出结果我就干掉了,正常来说Oracle 这种遥遥领先的数据库,不能100毫秒以内出结果都有问题。

简单看了下上面的计划 Predicate Information 谓词信息,里面信息很复杂,懒得解释(其实我也不懂为啥CBO为啥这样乱分组过滤),并没啥卵用,感觉很SB。

一句话就是CBO等价改写了 EXISTS 还有 :B1这种变量,每次都是传个值到:B1 然后进行filter , 重点是每次。反正各位读者以后在计划中看到这种 :B1 变量都是每次每次,就是一次一次的传值,比较完一个数据继续传。

这种按照 PG 的说法就是复杂的子连接无法提升,  GROUP BY b.name HAVING count(DISTINCT b.paragraph_id) = 1 惹得锅。

复杂的子连接无法提升参考 <<PostgreSQL技术内幕:查询优化深度探索 >>这本书 3.2篇章。

 

加个HINT:

SELECT a.* FROM hhhhhh a
WHERE   a.name IN (
    SELECT  /*+ unnest */ name from hhhhhh b
    GROUP BY b.name HAVING count(DISTINCT b.paragraph_id) = 1
  5  );

    ID NAME         PARAGRAPH_ID
---------- -------------------- ------------
    200002 aaaaa            10000001

Elapsed: 00:00:00.05

Plan hash value: 3353221841
 
-------------------------------------------------------------------------------------
| Id  | Operation               | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |           |     1 |    50 |     5  (20)| 00:00:01 |
|*  1 |  HASH JOIN SEMI         |           |     1 |    50 |     5  (20)| 00:00:01 |
|   2 |   TABLE ACCESS FULL     | HHHHHH    |     1 |    38 |     2   (0)| 00:00:01 |
|   3 |   VIEW                  | VW_NSO_1  |     1 |    12 |     3  (34)| 00:00:01 |
|*  4 |    FILTER               |           |       |       |            |          |
|   5 |     HASH GROUP BY       |           |     1 |    25 |     3  (34)| 00:00:01 |
|   6 |      VIEW               | VM_NWVW_2 |     1 |    25 |     3  (34)| 00:00:01 |
|   7 |       HASH GROUP BY     |           |     1 |    25 |     3  (34)| 00:00:01 |
|   8 |        TABLE ACCESS FULL| HHHHHH    |     1 |    25 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
"   1 - access(""A"".""NAME""=""NAME"")"
"   4 - filter(COUNT(""$vm_col_1"")=1)"

使用HINT将子链接强行提升(展开)以后,秒出。

 

但是使用HINT容易将执行计划固定住,非必要情况下不推荐。

等价改写该SQL 方式1:

SELECT A.*
FROM HHHHHH A
         INNER JOIN (SELECT COUNT(1) BB, NAME
                     FROM HHHHHH B
  5                       GROUP BY NAME) B ON A.NAME = B.NAME AND B.BB = 1;

    ID NAME         PARAGRAPH_ID
---------- -------------------- ------------
    200002 aaaaa            10000001

Elapsed: 00:00:00.03

Plan hash value: 3909860973
 
--------------------------------------------------------------------------------
| Id  | Operation             | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |        |     1 |    50 |     5  (20)| 00:00:01 |
|*  1 |  HASH JOIN            |        |     1 |    50 |     5  (20)| 00:00:01 |
|   2 |   TABLE ACCESS FULL   | HHHHHH |     1 |    38 |     2   (0)| 00:00:01 |
|   3 |   VIEW                |        |     1 |    12 |     3  (34)| 00:00:01 |
|*  4 |    FILTER             |        |       |       |            |          |
|   5 |     HASH GROUP BY     |        |     1 |    12 |     3  (34)| 00:00:01 |
|   6 |      TABLE ACCESS FULL| HHHHHH |     1 |    12 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
"   1 - access(""A"".""NAME""=""B"".""NAME"")"
   4 - filter(COUNT(*)=1)

改写成 join 以后也是秒出。

 

等价改写该SQL 方式2:

SELECT X.ID,
       X.NAME,
       X.PARAGRAPH_ID
FROM (SELECT A.*, COUNT(DISTINCT PARAGRAPH_ID) OVER (PARTITION BY NAME) CNT FROM HHHHHH A) X
  5  WHERE X.CNT = 1;

    ID NAME         PARAGRAPH_ID
---------- -------------------- ------------
    200002 aaaaa            10000001

Elapsed: 00:00:00.07


Plan hash value: 2750561680
 
------------------------------------------------------------------------------
| Id  | Operation           | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |        |     1 |    51 |     3  (34)| 00:00:01 |
|*  1 |  VIEW               |        |     1 |    51 |     3  (34)| 00:00:01 |
|   2 |   WINDOW SORT       |        |     1 |    38 |     3  (34)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| HHHHHH |     1 |    38 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
"   1 - filter(""X"".""CNT""=1)"

改写成开窗函数以后也是秒出。

 

 <<PostgreSQL技术内幕:查询优化深度探索 >> 这本书是真的不错,偷偷刷了好几次,每次看完都有新的理解。

标签:00,01,NAME,CBO,HHHHHH,Oracle,脑残,ID,SELECT
From: https://www.cnblogs.com/yuzhijian/p/18156615

相关文章

  • Oracle以及PG中将指定用户的primary,unique索引按照指定格式输出
    ---OracleSELECTLOWER(c.table_name)||':'||LOWER(i.index_name)||':'||LOWER(wm_concat(c.column_name))ASoutputFROMall_indexesiJOINall_ind_columnscONi.index_name=c.index_nameANDi.table_name=c.table_na......
  • oracle连接数据库报错ORA-12541:TNS:无监听程序
    最近闲来无事修改了电脑的用户名,本来以为不会影响什么,后来发现oracle数据库连接不上了,报错如下图:查看服务发现确实停止了,启动也启动不起来了搜索NetManager查看配置, 发现配置里面是我修改前的电脑名,才发现问题所在,随后我又把电脑名称改回来了数据库才能正常连接修改用户......
  • oracle select语句造成锁行的问题
    1、select*from表where codein(1,2,3);2、update表setcode = 4wherecode=1;以上两步是平时可能用到的写法,先查询在修改(理解我意思就行)。如果在高并发的情况下,这种情况更容易发生。select语句使用非聚族索引查询,会对非聚族索引添加共享锁,由于非聚族索引上没有se......
  • cls_oracle_logs.sh脚本遭遇TNS-12508错误浅析
    cls_oracle_logs.sh脚本的输出日志中有TNS-12508错误,具体如下所示........................................................................LSNRCTL> Current Listener is gspLSNRCTL> Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=gsp)))TNS-12508: TNS:......
  • oracle RAC加盘踢盘
    环境:OS:Centos7db:12CR2 1.主机层面加盘虚拟机加盘需要停掉数据库和主句,生产环境之间划分lun给到主机,不需要重启.multipath-ll找到新加磁盘的UUID 2.配置多路径每个节点上都要操作[root@rac02~]#more/etc/multipath.confblacklist{devnode"^sda"}defa......
  • Oracle数据库出现WARNING: too many parse errors告警的分析思路
    Oracle数据库的告警日志中出WARNING:toomanyparseerrors这些告警信息的话,如果遇到这个问题,我们应该如何分析呢?下面简单聊一下如何分析这个错误。该告警信息其实是12.2版本中的一个特性增强。在以前的Oracle版本中,数据库出现了解析错误时,数据库的alert日志中不会有任何相关的......
  • Oracle OCR,Vote disk故障恢复方案
     Oracle OCR,Votedisk故障恢复方案 1  概述OCR/Votedisk磁盘组故障场景恢复操作方案。测试版本:11.2.0.4.0适用于以下场景:操作维护OCR/Votedisk对应的磁盘组,提前备份了OCR文件信息,以便存在回退维护的空间。由于某些原因导致OCR/Votedisk的磁盘组丢失从而......
  • TIMESTAMP WITH TIME ZONE in PG and Oracle
    BothOracleandPostgreSQLsupporttheTIMESTAMPWITHTIMEZONEdatatype,buttherearesomedifferencesinhowtheyhandleandstoretimezoneinformation.Storage:Oracle:InOracle,TIMESTAMPWITHTIMEZONEvaluesarestoredinUTCinternally.Thet......
  • ORACLE定时器,执行周期设定
    对于DBA来说,数据库Job再熟悉不过了,因为经常要数据库定时的自动执行一些脚本,或做数据库备份,或做数据的提炼,或做数据库的性能优化,包括重建索引等等的工作。但是,Oracle定时器Job时间的处理上,千变万化,今天我把比较常用写法汇总如下:在总结之前,先把Job的参数一一说明一下:job参数是由Su......
  • oracle 修改密码不过期
    1.以sysdba身份进入sqlplus模式 2.查看用户密码的有效期设置一般默认的配置文件是DEFAULTSELECT*FROMdba_profilesWHEREprofile='DEFAULT'ANDresource_name='PASSWORD_LIFE_TIME';3.将密码有效期修改成“无限期”ALTERPROFILEDEFAULTLIMITPASSWORD_LIFE_TIM......