Salut tout le monde,
Parfois on veut que Oracle lise systématiquement les blocs d'une table non pas depuis le Database Buffer Cache, s'ils sont présents, mais depuis le disque dur.
Quel est l'intérêt? Tester les débits en lecture du disque dur! Cela arrive quand on a tuné un SELECT dans la base, qu'on estime ne plus rien pouvoir faire en tant que DBA et qu'il faut maintenant tuner le hardware, notamment les disques avec l'utilitaire Linux Hdparm.
Le problème est que, suite au premier SELECT, les blocs de la table, lus depuis le disque dur, sont mis en mémoire dans la SGA. Tous les prochains SELECTs liront donc ces blocs depuis la mémoire donc les modifications système apportées sur le tuning des disques durs ne pourront pas être testées.
Comment faire?
Une solution brutale est de vider TOUT le Database Buffer Cache avec la commande ALTER SYSTEM FLUSH buffer_cache; mais cela va ralentir les SELECTs sur les autres tables puisque pour TOUS les users il va falloir relire les données depuis le disque. Et puis c'est trop brutal! Quand je tune un ordre, mon objectif est de faire de la micro-chirurgie, mes tests doivent impacter au strict minimum voir pas du tout les autres users.
Donc comment faire pour lire TOUJOURS les données depuis le disque dur sans avoir à flusher le buffer cache?
La réponse serait de modifier le paramètre caché _serial_direct_read en lui mettant la valeur "always" et faire du parallélisme.
Sauf que, avec mes tests, c'est plus compliqué que prévu et c'est là que j'ai besoin de vos lumières
Voici mes tests.
On utilise Autotrace pour voir les lectures physiques et logiques.
On voit bien que la deuxième fois qu'il n'y a aucun accès au disque dur avec "0 physical reads" puisque j'ai fais un SELECT précédemment.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 SQL> select * from zztest; 20000 rows selected. SQL> set autotrace on
Si on flush le buffer cache, on revoit les lectures sur le disque dur avec "354 physical reads". Problème, cela arrive pour TOUTES les tables, donc on ralentit les autres SELECTs.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 SQL> select * from zztest; ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 20000 | 2441K| 101 (0)| 00:00:01 | | 1 | TABLE ACCESS FULL| ZZTEST | 20000 | 2441K| 101 (0)| 00:00:01 | ---------------------------------------------------------------------------- Statistics ---------------------------------------------------------- 0 recursive calls 4 db block gets 1668 consistent gets 0 physical reads 0 redo size 2958855 bytes sent via SQL*Net to client 15270 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 20000 rows processed
Si je relance, que des lectures en SGA : OK.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 SQL> alter system flush buffer_cache; System altered. SQL> select * from zztest; Statistics ---------------------------------------------------------- 0 recursive calls 4 db block gets 1668 consistent gets 355 physical reads 0 redo size 2958855 bytes sent via SQL*Net to client 15270 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 20000 rows processed
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 SQL> select * from zztest; Statistics ---------------------------------------------------------- 0 recursive calls 4 db block gets 1668 consistent gets 0 physical reads 0 redo size 2958855 bytes sent via SQL*Net to client 15270 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 20000 rows processed
Pour forcer une lecture sur disque dur sans flusher le buffer cache, on peut utiliser le hint PARRALEL car celui-ci force ce qu'on appelle le "direct path read".
Ah non, KO... Visiblement il ne faut pas croire TOUT ce qu'on lit sur le net :-)
Vous noterez que sur le plan d'exécution il n'y a pas de parallélisme car mon PC de test a un seul CPU et un seul core... mais cela n'est pas important puisque le direct read path sera bien pris en compte plus loin (sauf erreur de ma part)
En cherchant mieux sur le net, voilà ce que j'ai touvé : il faut AUSSI positionner le paramètre caché _serial_direct_read à "always" (même si celui-ci semble être utilisé surtout par Exadata.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 SQL> select /*+ PARRALEL */ * from zztest; ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 20000 | 2441K| 101 (0)| 00:00:01 | | 1 | TABLE ACCESS FULL| ZZTEST | 20000 | 2441K| 101 (0)| 00:00:01 | ---------------------------------------------------------------------------- Statistics ---------------------------------------------------------- 1 recursive calls 4 db block gets 1668 consistent gets 0 physical reads 0 redo size 2958855 bytes sent via SQL*Net to client 15270 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 20000 rows processed
Attention, c'est un paramètre caché donc il faut contacter le support avant de modifier celui-ci en production. MAIS comme la modification est là pour du tuning, vous ne devriez pas le modifier en prod.
Encore KO...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 SQL> ALTER SESSION SET "_serial_direct_read" = always; Session altered.
A noter que lors de mes tests, je constate deux choses :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 SQL> select * from zztest; Statistics ---------------------------------------------------------- 0 recursive calls 4 db block gets 1668 consistent gets 0 physical reads 0 redo size 2958855 bytes sent via SQL*Net to client 15270 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 20000 rows processed SQL> select /*+ PARRALEL */ * from zztest; Statistics ---------------------------------------------------------- 0 recursive calls 4 db block gets 1668 consistent gets 0 physical reads 0 redo size 2958855 bytes sent via SQL*Net to client 15270 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 20000 rows processed
- la lecture sur le disque dur ne se fait pas immédiatement, il y a un délai entre l'ALTER SESSION et la prise en compte du changement de paramètre... bizarre, cela rend les tests décrits ici plus difficilement reproductibles
- si le select /*+ PARRALEL */ * from zztest; ne provoque pas une lecture physique, essayez select * /*+ PARRALEL */ from zztest; en changeant de place le caractère *, même si cela va à l'encontre de la syntaxe des hints... oui, c'est compliqué et pas hyper fiable comme méthode... j'y comprends plus rien mais je n'abandonne pas
Si je relance le SELECT sans parallélisme : aïe, on lit les blocs depuis la SGA!
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 SQL> select * /*+ PARRALEL */ from zztest; Statistics ---------------------------------------------------------- 1 recursive calls 3 db block gets 1665 consistent gets 353 physical reads 0 redo size 2958855 bytes sent via SQL*Net to client 15270 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 20000 rows processed
Avec le hint PARALLEL c'est OK : visiblement il faut les deux conditions pour forcer la lecture sur disque dur : le paramètre "_serial_direct_read" à always et faire du parallélisme.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 SQL> select * from zztest; Statistics ---------------------------------------------------------- 0 recursive calls 4 db block gets 1668 consistent gets 0 physical reads 0 redo size 2958855 bytes sent via SQL*Net to client 15270 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 20000 rows processed
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 SQL> select * /*+ PARRALEL */ from zztest; Statistics ---------------------------------------------------------- 0 recursive calls 3 db block gets 1665 consistent gets 353 physical reads 0 redo size 2958855 bytes sent via SQL*Net to client 15270 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 20000 rows processed
Bon, voilà... si vous pouvez m'éclaircir sur ce qui se passe ou me proposer une autre solution, je suis preneur.
Bonne journée à tous et merci pour vos réponses
Partager