Suite à un échange à propos du fonctionnement du CBO et plus particulièrement sur les choix de parcours d’index en full scan ou en fast full scan sur le forum developpez.net qui était de la teneur suivante :
Intervenant 1 – Est-il vrai que si on effectue le requête select ename from emp, oracle n’utilisera pas l’index même si celui-ci existe ?
Intervenant 2 – Vrai sauf si la colonne ename ne peut pas être nulle et ce par contrainte, auquel cas un index full scan sera choisi
Intervenant 3 – Ne vouliez vous pas dire index fast full scan ?
Intervenant 2 – Non :
connect scott/tiger alter table emp modify ename not null; select ename from emp ; select * from table( dbms_xplan.display_cursor() ); PLAN_TABLE_OUTPUT --------------------------------------------------------------------------------------- Plan hash value: 1405736511 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 14 | 84 | 1 (0)| 00:00:01 | | 1 | INDEX FULL SCAN | EMP_T_IX1 | 14 | 84 | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------Intervenant 3 – Oui mais sur une table plus grosse ça fait du fast full scan (démo à l’appui) donc si.
Intervenant 2 – Peut être mais la question c’est sur la table emp et pas sur une autre donc relire l’énnoncé et donc re-non
Intervenant 4 – C’est possible aussi sur la table empconnect scott/tiger alter table emp modify ename not null; select ename from emp ; select * from table( dbms_xplan.display_cursor() ); PLAN_TABLE_OUTPUT --------------------------------------------------------------------------------------- Plan hash value: 3610277708 -------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | INDEX FAST FULL SCAN| IDX_EMP_ENAME | 14 | 84 | 2 (0)| 00:00:01 | --------------------------------------------------------------------------------------
La problématique qui se situe derrière cet échange est pourquoi dans un cas un index full scan est choisi et pas dans l’autre et quelle est au juste la différence entre un index full scan et un index fast full scan ?
La différence profonde entre un index full scan et un index fast full scan réside principalement dans la manière d’aller lire les blocs de données sur disque, le premier effectuera des accès monoblocs (ou db file sequential read) et arborescents alors que le second effectuera des accès multiblocs (ou db file scattered read) et séquentiels sur les seules feuilles de l’index, accès censés être plus rapides dès lors qu’il y a plus d’un bloc à lire.
Le premier plan (celui de l’intervenant 2) utilise un index monobloc tandis que le second plan (celui de l’intervenant 4) utilise un index de deux blocs feuilles.
Comment le choix s’est-il opéré ?
Si on reproduit le premier test :
drop index IDX_EMP_ENAME ; create index IDX_EMP_ENAME on emp(ename) ; alter session set tracefile_identifier=Monobloc_idx ; alter system flush shared_pool ; alter session set events='10053 trace name context forever' ; select ename from emp ; alter session set events='10053 trace name context off' ;
La lecture de la trace effectuée et plus particulièrement des données liées aux statistiques sur les objets et au mode d’accès aux données présentent les informations suivantes :
*************************************** BASE STATISTICAL INFORMATION *********************** Table Stats:: Table: EMP Alias: EMP #Rows: 14 #Blks: 5 AvgRowLen: 38.00 ChainCnt: 0.00 Index Stats:: Index: IDX_EMP_ENAME Col#: 2 LVLS: 0 #LB: 1 #DK: 14 LB/K: 1.00 DB/K: 1.00 CLUF: 1.00 Index: PK_EMP Col#: 1 LVLS: 0 #LB: 1 #DK: 14 LB/K: 1.00 DB/K: 1.00 CLUF: 1.00 Access path analysis for EMP *************************************** SINGLE TABLE ACCESS PATH Single Table Cardinality Estimation for EMP[EMP] Table: EMP Alias: EMP Card: Original: 14.000000 Rounded: 14 Computed: 14.00 Non Adjusted: 14.00 Access Path: TableScan Cost: 3.00 Resp: 3.00 Degree: 0 Cost_io: 3.00 Cost_cpu: 37987 Resp_io: 3.00 Resp_cpu: 37987 Access Path: index (index (FFS)) Index: IDX_EMP_ENAME resc_io: 2.00 resc_cpu: 8801 ix_sel: 0.000000 ix_sel_with_filters: 1.000000 Access Path: index (FFS) Cost: 2.00 Resp: 2.00 Degree: 1 Cost_io: 2.00 Cost_cpu: 8801 Resp_io: 2.00 Resp_cpu: 8801 Access Path: index (FullScan) Index: IDX_EMP_ENAME resc_io: 1.00 resc_cpu: 9921 ix_sel: 1.000000 ix_sel_with_filters: 1.000000 Cost: 1.00 Resp: 1.00 Degree: 1 Best:: AccessPath: IndexRange Index: IDX_EMP_ENAME Cost: 1.00 Degree: 1 Resp: 1.00 Card: 14.00 Bytes: 0
On constate en bleu que les statistiques de l’index montrent que celui-ci est bien constitué d’un unique bloc feuille (#LB pour Number of Leaf-Blocks), l’information se retrouve dans la colonne blocks de la vue user_indexes.
Si on compare les coût d’accès non parallèles (Cost_io)1 en rouge ou vert on s’apperçoit qu’il choisi l’accès le moins coûteux, certes, mais surtout qu’il compte un accès à 2 blocs en cas d’index fast full scan ce qui peut paraitre un peu fort de café alors que de son propre aveux, Oracle ne comptabilise qu’un seul bloc pour l’index. La seule explication raisonnable que je peux voire ici, c’est que pour une entrée/sortie multibloc il lira forcément un minimum de deux blocs ce qui est plus que logique.
Si on reproduit le second test :
drop index IDX_EMP_ENAME ; create index IDX_EMP_ENAME on emp(ename) pctfree 96 ; alter session set tracefile_identifier=Multibloc_idx ; alter system flush shared_pool ; alter session set events='10053 trace name context forever' ; select ename from emp ; alter session set events='10053 trace name context off' ;
La lecture de la trace effectuée et plus particulièrement des données liées aux statistiques sur les objets et au mode d’accès aux données présentent les informations suivantes :
BASE STATISTICAL INFORMATION *********************** Table Stats:: Table: EMP Alias: EMP #Rows: 14 #Blks: 5 AvgRowLen: 38.00 ChainCnt: 0.00 Index Stats:: Index: IDX_EMP_ENAME Col#: 2 LVLS: 1 #LB: 2 #DK: 14 LB/K: 1.00 DB/K: 1.00 CLUF: 1.00 Index: PK_EMP Col#: 1 LVLS: 0 #LB: 1 #DK: 14 LB/K: 1.00 DB/K: 1.00 CLUF: 1.00
Ici le nombre de blocs feuille est de 2 et le nombre total de blocs d’index de 3 (une racine et deux feuilles).
Access path analysis for EMP *************************************** SINGLE TABLE ACCESS PATH Single Table Cardinality Estimation for EMP[EMP] Table: EMP Alias: EMP Card: Original: 14.000000 Rounded: 14 Computed: 14.00 Non Adjusted: 14.00 Access Path: TableScan Cost: 3.00 Resp: 3.00 Degree: 0 Cost_io: 3.00 Cost_cpu: 37987 Resp_io: 3.00 Resp_cpu: 37987 Access Path: index (index (FFS)) Index: IDX_EMP_ENAME resc_io: 2.00 resc_cpu: 15923 ix_sel: 0.000000 ix_sel_with_filters: 1.000000 Access Path: index (FFS) Cost: 2.00 Resp: 2.00 Degree: 1 Cost_io: 2.00 Cost_cpu: 15923 Resp_io: 2.00 Resp_cpu: 15923 Access Path: index (FullScan) Index: IDX_EMP_ENAME resc_io: 3.00 resc_cpu: 24164 ix_sel: 1.000000 ix_sel_with_filters: 1.000000 Cost: 3.00 Resp: 3.00 Degree: 1 ******** Begin index join costing ******** ****** trying bitmap/domain indexes ****** Access Path: index (FullScan) Index: IDX_EMP_ENAME resc_io: 3.00 resc_cpu: 24164 ix_sel: 1.000000 ix_sel_with_filters: 1.000000 Cost: 3.00 Resp: 3.00 Degree: 0 Access Path: index (FullScan) Index: IDX_EMP_ENAME resc_io: 3.00 resc_cpu: 24164 ix_sel: 1.000000 ix_sel_with_filters: 1.000000 Cost: 3.00 Resp: 3.00 Degree: 0 Bitmap nodes: Used IDX_EMP_ENAME Cost = 3.751463, sel = 1.000000 ****** finished trying bitmap/domain indexes ****** ******** End index join costing ******** Best:: AccessPath: IndexFFS Index: IDX_EMP_ENAME Cost: 2.00 Degree: 1 Resp: 2.00 Card: 14.00 Bytes: 0
C’est bien le parcours des feuilles d’index en mode multibloc qui est choisi.
- les coûts d’accès parallèles sont donnés par Resp_io