Oracle优化器成本的估算(10053 Trace中基于CPU成本模型的内容解析)

2022-08-22 13:39:13 浏览数 (1)

编者按:

本文作者系Walt,关注SQL开发,Oracle、MySQL、PostgreSQL、TiDB等数据库,AWS、Azure、OCI等公有云计算架构和技术。

个人主页: https://blog.csdn.net/lukeUnique。

SQL专栏课程:https://www.modb.pro/course/125

【免责声明】本号文章仅代表个人观点,与任何公司无关。

编辑|SQL和数据库技术(ID:SQLplusDB)

单表访问路径的评估

代码语言:javascript复制
Access path analysis for EMP1
***************************************
SINGLE TABLE ACCESS PATH 
  Single Table Cardinality Estimation for EMP1[E] 
  SPD: Return code in qosdDSDirSetup: NOCTX, estType = TABLE

 kkecdn: Single Table Predicate:"E"."EMPNO">90000
  Column (#1): EMPNO(NUMBER)
    AvgLen: 5 NDV: 101395 Nulls: 0 Density: 0.000010 Min: 0.000000 Max: 4.000000
  Estimated selectivity: 0.099995 , col: #1 
  Table: EMP1  Alias: E
    Card: Original: 101948.000000  Rounded: 10194  Computed: 10194.290235  Non Adjusted: 10194.290235
  Scan IO  Cost (Disk) =   238.000000
  Scan CPU Cost (Disk) =   22943498.560000
  Cost of predicates:
    io = NOCOST, cpu = 50.000000, sel = 0.099995 flag = 2048  ("E"."EMPNO">90000)
  Total Scan IO  Cost  =   238.000000 (scan (Disk))
                           0.000000 (io filter eval) (= 0.000000 (per row) * 101948.000000 (#rows))
                       =   238.000000
  Total Scan CPU  Cost =   22943498.560000 (scan (Disk))
                           5097400.000000 (cpu filter eval) (= 50.000000 (per row) * 101948.000000 (#rows))
                       =   28040898.560000
  Access Path: TableScan
    Cost:  240.132964  Resp: 240.132964  Degree: 0
      Cost_io: 238.000000  Cost_cpu: 28040899
      Resp_io: 238.000000  Resp_cpu: 28040899
  Best:: AccessPath: TableScan
         Cost: 240.132964  Degree: 1  Resp: 240.132964  Card: 10194.290235  Bytes: 0.000000

上面的内容是10053 Trace中对于单表访问路径的评估。

优化器成本的估算(基于CPU成本模型)

对于优化器成本的估算是一个复杂的过程,涉及到大量的的理论和内部操作。

Oracle对于成本的预估模型,也可以分为两种:

代码语言:javascript复制
CPU成本模型 :参考系统统计信息进行成本估算。
I/O成本模型 :仅通过I/O的次数进行成本估算。

成本模型的选择通过_optimizer_cost_model隐含参数控制。

代码语言:javascript复制
SQL> select a.ksppinm "PARAMETER",b.ksppstvl "VALUE",a.KSPPDESC "DESCRIPTION"
  2  from x$ksppi a, x$ksppcv b
  3  where a.indx = b.indx and
  4  a.ksppinm like '%_optimizer_cost_model%';


PARAMETER                                          VALUE      DESCRIPTION
-------------------------------------------------- ---------- --------------------------------------------------
_optimizer_cost_model                              CHOOSE     optimizer cost model

_optimizer_cost_model隐含参数的值主要由如下三个值,成本模型的选择会根据其值相应调整。

代码语言:javascript复制
- CHOOSE:默认值,这时候如果有系统统计信息的话会使用CPU成本模型 ,如果没有系统统计信息的话会使用I/O成本模型 。
- IO :使用基于I/O成本模型 。
- CPU :使用基于CPU成本模型 。

对于我们这个例子,是有系统统计信息的,并且使用的是没有负载的系统统计信息(NOWORKLOAD),所以使用的是基于CPU的成本模型计算访问表的成本。简单而言,基于CPU成本模型主要会考虑I/O和CPU两方面开销估算成本。

代码语言:javascript复制
总COST = IO_COST  CPU_COST

IO_COST主要包括单块读的成本和多块读的成本。

代码语言:javascript复制
IO_COST = (#SRDS * SREADTIM   #MRDS * MREADTIM) / SREADTIM 
其中:
- #SRDS (NUMBER OF SINGLE BLOCK READS )是单块读的次数 。
- #MRDS(NUMBER OF MULTI BLOCK READS)是多块读的次数,其值等于表中高水位线以下数据块数除以一次多块读的块数,即#Blks / MBRC。
- SREADTIM(SINGLE BLOCK READ TIME)是单块读平均时间(单位milliseconds 毫秒)。
- MREADTIM(MULTI BLOCK READ TIME )是多块读平均时间(单位milliseconds 毫秒)。

CPU_COST可以通过如下方法进行估算。

代码语言:javascript复制
CPU_COST=#CPUCycles / (CPUSPEED * 1000 * SREADTIM)
其中:
- #CPUCycles 是预估的CPU开销(周期数),可以通过PLAN_TABLE/V$SQL_PLAN的CPU_COST列或者10053 Trace中的Cost_cpu,本例中为Cost_cpu: 28040899。
- CPUSPEED(CPU cycles per second) 是CPU的频率(速度)。
- SREADTIM是单块读平均时间(单位milliseconds 毫秒)。

对于没有负载的系统统计信息(NOWORKLOAD),并不会收集SREADTIM和MREADTIM的值,一般通过如下方法计算其值。

代码语言:javascript复制
SREADTIM = IOSEEKTIM   DB_BLOCK_SIZE / IOTFRSPEED
MREADTIM = IOSEEKTIM   (DB_FILE_MULTIBLOCK_READ_COUNT * DB_BLOCK_SIZE) / IOTFRSPEED

**如果DB_FILE_MULTIBLOCK_READ_COUNT没有显式设置的话,Oracle会使用隐含参数_db_file_optimizer_read_count的值替代DB_FILE_MULTIBLOCK_READ_COUNT,
对于本例而言值为8。

SQL>  select a.ksppinm "PARAMETER",b.ksppstvl "VALUE",a.KSPPDESC "DESCRIPTION"
  2   from x$ksppi a, x$ksppcv b
  3   where a.indx = b.indx and
  4   a.ksppinm like '%_db_file_optimizer_read_count%';

PARAMETER                                          VALUE      DESCRIPTION
-------------------------------------------------- ---------- --------------------------------------------------
_db_file_optimizer_read_count                      8          multiblock read count for regular clients

另外,CPUSPEED即是CPUSPEEDNW的值。
CPUSPEED=CPUSPEEDNW

结合前面的系统统计信息的部分,我们可以带入如下的值:

代码语言:javascript复制
-----------------------------
SYSTEM STATISTICS INFORMATION
-----------------------------
Using dictionary system stats.
  Using NOWORKLOAD Stats
  CPUSPEEDNW: 1096 millions instructions/sec (default is 100)
  IOTFRSPEED: 4096 bytes per millisecond (default is 4096)
  IOSEEKTIM:  10 milliseconds (default is 10)
  MBRC:       NO VALUE blocks (default is 8)

 ====》》》
CPUSPEEDNW=1096
IOTFRSPEED=4096
IOSEEKTIM=10
MBRC = 8

--初始参数的值

代码语言:javascript复制
SQL> show parameter DB_BLOCK_SIZE

NAME                                 TYPE                   VALUE
------------------------------------ ---------------------- ------------------------------
db_block_size                        integer                8192

因此,我们可以计算出单块读和多块读的平均时间。

代码语言:javascript复制
单块读平均时间SREADTIM=10   8192/4096 =12  毫秒)
多块读平均时间MREADTIM=10  (8* 8192)/4096 =26 毫秒)

另外,通过上面的TRACE内容我们可以得到表中高水位线以下的数据块信息。

代码语言:javascript复制
***************************************
BASE STATISTICAL INFORMATION
***********************
Table Stats::
  Table: EMP1  Alias:  E
  #Rows: 101948  SSZ: 0  LGR: 0  #Blks:  874  AvgRowLen:  50.00  NEB: 0  ChainCnt:  0.00  ScanRate:  0.00  SPC: 0  RFL: 0  RNF: 0  CBK: 0  CHR: 0  KQDFLG: 1
  #IMCUs: 0  IMCRowCnt: 0  IMCJournalRowCnt: 0  #IMCBlocks: 0  IMCQuotient: 0.000000
  
 ====》》》
  数据块数#Blks = 874
  多块读#MRDS = #Blks / MBRC =874 / 8

对于全表扫描(TABLE FULL SCAN)而言,尽管读取表中管理块信息等情况时也可能发生单块读,但成本估算时一般全表扫描可以认为都是多块读,单块读#SRDS为0。但是为了减少优化器的估算误差,Oracle通过隐含参数_table_scan_cost_plus_one来调节全表扫描和索引快速扫描的成本。当_table_scan_cost_plus_one为TRUE时,会默认在计算成本时加1。

代码语言:javascript复制
SQL> select a.ksppinm "PARAMETER",b.ksppstvl "VALUE",a.KSPPDESC "DESCRIPTION"
  2  from x$ksppi a, x$ksppcv b
  3  where a.indx = b.indx and
  4  a.ksppinm like '%_table_scan_cost_plus_one%';


PARAMETER                 VALUE      DESCRIPTION
------------------------- ---------- ------------------------------------------------------------
_table_scan_cost_plus_one TRUE       bump estimated full table scan and index ffs cost by one

综上,我们可以进一步计算出全表访问DEPT1表的COST。

代码语言:javascript复制
总COST = IO_COST  CPU_COST

IO_COST  =(#SRDS * SREADTIM   #MRDS * MREADTIM) / SREADTIM 
             = #SRDS    #MRDS * MREADTIM/ SREADTIM 
             = 1   #Blks / MBRC * MREADTIM/ SREADTIM
             = 1   874 / 8 *26 /12  
             = 1   236.708333
             = 238
                      
CPU_COST = #CPUCycles / (CPUSPEED * 1000 * SREADTIM)
              =  28040899 / (1096*1000*12)
               = 2.13206349

所以最终结果的总Cost 为240.13206349左右。

与Cost: 240.132964大致相同。

0 人点赞