はじめに
10053トレースは、OracleDB のオプティマイザがどのような情報を元に、どのような計算や判断をして最終的な実行計画を選んだかが記録されたトレースファイルです。
似たものに10046トレースがありますが、10046トレースは SQL実行時の詳細情報 (待機イベントや I/O、バインド値など) が記録されたトレースファイルです。
どちらも普段から見るようなファイルではなく、サポートなどからリクエストされて取るケースがほとんどかと思います。
簡単な SQL でも 3,000行以上の出力があり、読み解くにも一定のスキル・知識・経験が必要でなかなか読む機会はないと思いますが、オプティマイザの理解につながる一番の資料でもあるため、簡単にはなりますが解説します。
環境は、OCI のコンピュート・インスタンスに導入した OracleDB 19c を使用しています。
前提
トレース対象の SQL は下記の通りです。
SQL> SELECT * FROM p3_order_items WHERE product_id=3165
テーブル (p3_order_items) は Oracle が公開しているサンプルスキーマのテーブル (order_items) を元にしており、SQL実行前に頻度ヒストグラムありでオプティマイザ統計を取得しています。
その他の情報は下記の通り。
SQL> select count(1) from p3_order_items;
COUNT(1)
----------
1000665
SQL>
SQL> select * from p3_order_items fetch first 5 rows only;
ORDER_ID LINE_ITEM_ID PRODUCT_ID UNIT_PRICE QUANTITY
---------- ------------ ---------- ---------- ----------
2423 7 3290 65 33
2426 5 3252 25 29
2427 7 2522 40 22
2428 11 3173 86 28
2429 10 3165 36 67
SQL> select table_name,index_name from user_indexes where table_name like 'P3%';
TABLE_NAME
------------------------------------------------------------------------------------------------------------------------
INDEX_NAME
------------------------------------------------------------------------------------------------------------------------
P3_ORDER_ITEMS
P3OI_PID_IX
SQL> select table_name,column_name,avg_col_len from user_tab_col_statistics
2 where table_name like 'P3%' order by 2;
TABLE_NAME
------------------------------------------------------------------------------------------------------------------------
COLUMN_NAME
------------------------------------------------------------------------------------------------------------------------
AVG_COL_LEN
-----------
P3_ORDER_ITEMS
LINE_ITEM_ID
3
P3_ORDER_ITEMS
ORDER_ID
6
P3_ORDER_ITEMS
PRODUCT_ID
4
P3_ORDER_ITEMS
QUANTITY
4
P3_ORDER_ITEMS
UNIT_PRICE
5
SQL>
3,000行以上の解説は大変なので、一番の肝である「オプティマイザがどのような情報を元に、どのような計算や判断をして最終的な実行計画を選んだか」が分かる箇所を抜粋しました。こちらをベースに解説します。(非公開情報もあるため、歯切れが悪い部分もありますがご了承ください)
10053トレース (抜粋)
***************************************
BASE STATISTICAL INFORMATION
***********************
Table Stats::
Table: P3_ORDER_ITEMS Alias: P3_ORDER_ITEMS
#Rows: 1000665 SSZ: 0 LGR: 0 #Blks: 3659 AvgRowLen: 21.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
Index Stats::
Index: P3OI_PID_IX Col#: 3
LVLS: 2 #LB: 2094 #DK: 185 LB/K: 11.00 DB/K: 20.00 CLUF: 3760.00 NRW: 1000665.00 SSZ: 0.00 LGR: 0.00 CBK: 0.00 GQL: 0.00 CHR: 0.00 KQDFLG: 8192 BSZ: 1
KKEISFLG: 1
try to generate single-table filter predicates from ORs for query block SEL$1 (#0)
finally: "P3_ORDER_ITEMS"."PRODUCT_ID"=3165
=======================================
SPD: BEGIN context at query block level
=======================================
Query Block SEL$1 (#0)
Return code in qosdSetupDirCtx4QB: NOCTX
=====================================
SPD: END context at query block level
=====================================
Access path analysis for P3_ORDER_ITEMS
***************************************
SINGLE TABLE ACCESS PATH
Single Table Cardinality Estimation for P3_ORDER_ITEMS[P3_ORDER_ITEMS]
SPD: Return code in qosdDSDirSetup: NOCTX, estType = TABLE
kkecdn: Single Table Predicate:"P3_ORDER_ITEMS"."PRODUCT_ID"=3165
Column (#3):
NewDensity:0.000000, OldDensity:0.000000 BktCnt:1000665.000000, PopBktCnt:1000593.000000, PopValCnt:113, NDV:185
Column (#3): PRODUCT_ID(NUMBER)
AvgLen: 4 NDV: 185 Nulls: 0 Density: 0.000000 Min: 1781.000000 Max: 3515.000000
Histogram: Freq #Bkts: 185 UncompBkts: 1000665 EndPtVals: 185 ActualVal: yes
Estimated selectivity: 1.0993e-05 , endpoint value predicate, col: #3
Table: P3_ORDER_ITEMS Alias: P3_ORDER_ITEMS
Card: Original: 1000665.000000rsel = 0.000011 Rounded: 11 Computed: 11.000000 Non Adjusted: 11.000000
Scan IO Cost (Disk) = 993.000000
Scan CPU Cost (Disk) = 216184138.960000
Cost of predicates:
io = NOCOST, cpu = 50.000000, sel = 0.000011 flag = 2048 ("P3_ORDER_ITEMS"."PRODUCT_ID"=3165)
Total Scan IO Cost = 993.000000 (scan (Disk))
+ 0.000000 (io filter eval) (= 0.000000 (per row) * 1000665.000000 (#rows))
= 993.000000
Total Scan CPU Cost = 216184138.960000 (scan (Disk))
+ 50033250.000000 (cpu filter eval) (= 50.000000 (per row) * 1000665.000000 (#rows))
= 266217388.960000
Access Path: TableScan
Cost: 1001.991752 Resp: 1001.991752 Degree: 0
Cost_io: 993.000000 Cost_cpu: 266217389
Resp_io: 993.000000 Resp_cpu: 266217389
****** Costing Index P3OI_PID_IX
SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_SCAN
SPD: Return code in qosdDSDirSetup: NOCTX, estType = INDEX_FILTER
Estimated selectivity: 1.0993e-05 , endpoint value predicate, col: #3
Access Path: index (AllEqRange)
Index: P3OI_PID_IX
resc_io: 4.000000 resc_cpu: 33216
ix_sel: 1.0993e-05 ix_sel_with_filters: 1.0993e-05
Cost: 4.001122 Resp: 4.001122 Degree: 1
Best:: AccessPath: IndexRange
Index: P3OI_PID_IX
Cost: 4.001122 Degree: 1 Resp: 4.001122 Card: 11.000000 Bytes: 0.000000
***************************************
BASE STATISTICAL INFORMATION
Table Stats
Rows
テーブルの行数を示しています。「前提」の SELECT COUNT の結果と一致しています。
Blks
テーブルのブロック数を示しています。
AvgRowLen
テーブルの平均行長を示しています。「前提」で USER_TAB_COL_STATISTICS の AVG_COL_LEN を SELECT していますが、これの総和です。
Index Stats
Col#
インデックスカラム (PRODUCT_ID) が、テーブル定義上何番目に位置するかを示します。
LVLS
B-tree の高さを示します。
LB
インデックスのブロック数を示します。(Leaf Blocks)
DK
キー値の種類数を示します。(Distinct Keys)
LB/K
1種類のキー値が平均でいくつのブロックにまたがっているかを示します。(LB/DK = 2,094/185 = 11)
DB/K
1つのキー値が分布するテーブルブロック数の平均を示します。(CLUF/DK = 3,760/185 = 20)
CLUF
インデックスの並びと、表の物理配置 (ROWID順) がどれだけ揃っているかを示しています。(Cluster Factor)
値の範囲はテーブルブロック数 (今回は 3,659) ~ 表の行数 (今回は 1,000,665) で、小さいほど物理的にまとまっており、大きいほど分散しているといえます。(今回は「物理的にまとまっている」)
Access path analysis
SINGLE TABLE ACCESS PATH
PopValCnt
キー値の中で、支配的なキー値の数を示します。(今回は 185種類のうち 113種類が支配的)
PopBktCnt
PopValCnt に該当するレコード数を示します。(1,000,665レコードのうち 1,000,593レコードが支配的 → 偏りが大きい)
Histogram
ヒストグラムタイプを示します。今回は頻度ヒストグラムを選択しているため、Freq(uency) となっています。
Estimated selectivity
セレクティビティ (WHERE句の条件で取得される行の割合) を示します。
今回はヒストグラムありでオプティマイザ統計を取得しているため、product_id=3165で取得される行が 11 であるとわかっており、11/1,000,665 = 1.0993e-05 となります。
Scan IO Cost (Disk), Scan CPU Cost (Disk)
システム統計に基づく値です。
Access Path: TableScan
Cost
テーブルスキャンの総コストを示しています。
Scan IO Cost + Scan CPU Cost の SREAD換算値で求められます。(993 + 8.99 = 1001.99)
Access Path: index
resc_io
インデックススキャンで読み込むブロック数を示します。
(LVLS + 1 + ceil(#LB * selectivity) = 3 + ceil(2,094 * 1.0993e-05) = 4)
resc_cpu
インデックススキャンに要する CPUコストを示します。
Cost
インデックススキャンの総コストを示します。
resc_io + resc_cpu の SREAD換算値で求められます。(4 + 0.001122 = 4.001122)
Best:: AccessPath
最終的に選択した実行計画 (オペレーション) を示します。
インデックススキャンの総コスト (4.001122) がテーブルスキャンの総コスト (1001.99) より遥かに小さいため、インデックススキャンが選択されていることがわかります。
別の機会に、JOIN をしている SQLなど様々なケースの 10053トレースを概説したいと思います ![]()