All Things Oracle

¿Cómo acelerar el acceso a los file systems?

Estas usando filesystems para tu base de datos y todo opera en aparente normalidad, pero ¿sabías que por defecto Oracle no aprovecha todas las mejoras que los filesystems modernos tienen implementados? Pues esa es la cruda realidad, si te interesa saber cómo puedes sacarle el jugo al I/O de tus discos con solo cambiar un parámetro, entonces debemos empezar por algo de teoría no Oracle.

File Buffer Cache

Un archivo no es más que una colección de bits almacenados en un medio persistente. Cuando un proceso requiere acceder a los datos de un archivo, el sistema operativo los lleva a la memoria principal, donde el proceso puede hacer uso del mismo, modificarlo, y luego solicitar que sea nuevamente guardado en disco. Pero teniendo en mente que los discos son mucho más lentos que la memoria principal, los sistemas operativos hacen normalmente uso de un buffer en memoria, llamado file buffer cache.

Como resultado, el sistema operativo primero intenta obtener los datos del buffer cache, si no los encuentra allí los lee de disco y la coloca en el buffer cache. De forma similar, las escrituras también pasan por el buffer cache, de forma que las futuras lecturas puedan ser satisfechas sin necesidad de acceder a los discos.

Direct I/O

Hasta acá el uso del file buffer cache parecería beneficioso, pero no olvidemos que Oracle ya tiene su propia implementación: el database buffer cache. Al ser el propio Oracle quien controla qué bloques requieren de permanecer o no en el cache, bajo un algoritmo LRU con el que no cuenta el sistema operativo, la existencia del file buffer cache puede resultar indeseable e innecesario al tener que viajar los datos primero al file buffer cache y luego al database buffer cache, conllevando a un consumo adicional de CPU y también de memoria principal, la cual ya no está disponible para Oracle.

Tomando esto en cuenta, hace su aparición el Direct I/O, como una forma de evitar el uso del file buffer cache, de hecho es la forma con que se interactúa con los raw devices (porciones de disco no formateados); en la actualidad prácticamente todos los sistemas operativos soportan direct I/O y en algunos casos incluso versiones más sofisticadas como concurrent i/o provisto por IBM con JFS2.

Habilitando Direct I/O

En Oracle existe una forma de controlar la forma en que se interactúa con los filesystems, se trata de filesystemio_options, que acepta como valores:
none

La grabación será síncrona con bloqueo, es decir la aplicación espera a que la llamada al sistema se complete antes de poder hacer una nueva llamada. Es universalmente soportada pero es la más forma más lenta.

asynchLa aplicación no espera a que la llamada al sistema se complete, puede realizar otras tareas mientras espera la confirmación de la llamada previa.
directio

La grabación es síncrona sin pasar por el file buffer cache (las dos modalidades previas hacen uso del file buffer cache).

setall

La grabación es asíncrona y sin pasar por el file buffer cache. Presenta la posibilidad de máximo desempeño.

Dependiendo de la plataforma, Oracle le asigna a este parámetro el valor por defecto none (ejm. Linux) o asynch (ejm. Solaris).

Para habilitar direct I/O los valores a escoger han de ser directio o mejor aún setall (asynch + directio).

Este parámetro no se puede cambiar online, por lo que luego de aplicar el cambio en el archivo spfile.ora debes reiniciar la base de datos para que entre en efecto.

SYS@orcl> show parameter filesystemio_options

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
filesystemio_options                 string      none

SYS@orcl> alter system set filesystemio_options=setall scope=spfile;

System altered.

SYS@orcl> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.

SYS@orcl > startup
ORACLE instance started.

Total System Global Area  209715200 bytes
Fixed Size                  1279360 bytes
Variable Size             109054592 bytes
Database Buffers           96468992 bytes
Redo Buffers                2912256 bytes
Database mounted.
Database opened.

Estamos probando con Linux, de allí el valor none, ahora para verificar que estamos trabajando con asynch y directio en simultáneo, podemos valernos de algunos utilitarios propios de Linux, pero que tienen sus similares en otros sistemas operativos.

Primero veamos la situación original, es decir cuando filesystemio_options=none.

SYS@orcl> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SYS@orcl> startup mount;
ORACLE instance started.

Total System Global Area  205520896 bytes
Fixed Size                  1266608 bytes
Variable Size             142609488 bytes
Database Buffers           58720256 bytes
Redo Buffers                2924544 bytes
Database mounted.

$ ps -ef | grep dbw
oracle   28532     1  0 16:56 ?        00:00:00 ora_dbw0_orcl
SYS@orcl> alter database open;

Database altered.
$ more /tmp/trace_dbwr.out
. . .
open("/u02/oradata/ORCL/datafile/o1_mf_system_3wqn5ypm_.dbf", O_RDWR|O_SYNC|O_LARGEFILE) = 17
. . .

$ cat /proc/slabinfo | grep kio
kioctx   18  30  256  15  1 : tunables  120  60  0 : slabdata  2  2  0
kiocb     0   0  128  31  1 : tunables  120  60  0 : slabdata  0  0  0

Ahora con filesystemio_options=setall.

$ more /tmp/trace_dbwr.out
. . .
open("/u02/oradata/ORCL/datafile/o1_mf_system_3wqn5ypm_.dbf", O_RDONLY|O_DIRECT|O_LARGEFILE) = 17
. . .
$ cat /proc/slabinfo | grep kio
kioctx   17  30  256  15  1 : tunables  120  60  0 : slabdata  2  2 0
kiocb     7  31  128  31  1 : tunables  120  60  0 : slabdata  1  1 0
Que DBWR abra los datafiles con O_DIRECT es señal del uso de Direct I/O, mientras que la presencia de valores distintos de cero para kiocb lo es del uso de Asynch I/O.

¿File buffer cache o Direct I/O?

La decisión no es simple, antes de optar por uno u otro debemos tener en cuenta cómo puede afectar a nuestras aplicaciones, para empezar vemos el caso de una aplicación simulada que es intensiva en lecturas aleatorias, lo cual es típico de las aplicaciones OLTP.
$ cat Random.sh
sqlplus / as sysdba << EOF
alter system flush buffer_cache;
exit;
EOF
sqlplus /nolog @Random.sql &
sqlplus /nolog @Random.sql &
sqlplus /nolog @Random.sql &
sqlplus /nolog @Random.sql &

$ cat Random.sql
connect test/test
set timing on
alter session set events '10046 trace name context forever, level 8';
begin
  for i in 1..200000 loop
     execute immediate
        'select data from testio where id = :id'
     using ROUND(dbms_random.value(1,200000));
  end loop;
end;
/
exit
Analizando los tiempos con tkprof:
filesystemio_options=none

select data
from
 testio where id = :id

call      count     cpu  elapsed  disk  query  current  rows
------- -------  ------ -------- ----- ------ -------- -----
Parse         4    0.00     1.38     0      0        0     0
Execute  800000   51.29   172.13     1      1        0     0
Fetch         0    0.00     0.00     0      0        0     0
------- -------  ------ -------- ----- ------ -------- -----
total    800004   51.30   173.51     1      1        0     0

===
filesystemio_options=setall

select data
from
 testio where id = :id

call      count     cpu  elapsed  disk  query  current  rows
------- -------  ------ -------- ----- ------ -------- -----
Parse         4    0.00     0.00     0      0        0     0
Execute  800000   38.84   128.18     0      0        0     0
Fetch         0    0.00     0.00     0      0        0     0
------- -------  ------ -------- ----- ------ -------- -----
total    800004   38.85   128.18     0      0        0     0

Ahora con una aplicación intensiva en full table scans, típico de las aplicaciones DSS.

$ cat Full.sh
sqlplus / as sysdba << EOF
alter system flush buffer_cache;
exit;
EOF
sqlplus /nolog @Full.sql &
sqlplus /nolog @Full.sql &
sqlplus /nolog @Full.sql &
sqlplus /nolog @Full.sql &

$ cat Full.sql
connect test/test
set timing on
alter session set events '10046 trace name context forever, level 8';
declare v_data testio.data%type;
begin
for i in 1..25 loop
select max(data) into v_data from testio;
end loop;
end;
/
exit
Resumen de traces analizados con tkprof.
filesystemio_options=none

SELECT MAX(DATA)
FROM
 TESTIO

call     count     cpu  elapsed    disk    query  current  rows
------- ------  ------ -------- ------- -------- -------- -----
Parse        4    0.00     0.00       0        0        0     0
Execute    100    0.01     0.01       0        0        0     0
Fetch      100   61.89   232.97  503813  1446600        0   100
------- ------  ------ -------- ------- -------- -------- -----
total      204   61.91   232.99  503813  1446600        0   100

Elapsed times include waiting on following events:
  Event waited on               Times   Max. Wait  Total Waited
  --------------------------   Waited  ----------  ------------
  db file sequential read         146        0.00          0.00
  db file scattered read        13771        0.67         19.53 

====
filesystemio_options=setall

SELECT MAX(DATA)
FROM
 TESTIO

call     count     cpu  elapsed    disk    query  current  rows
------- ------  ------ -------- ------- -------- -------- -----
Parse        4    0.01     1.22       1        1        0     0
Execute    100    0.02     0.06       0        0        0     0
Fetch      100   70.63   366.62  259438  1446600        0   100
------- ------  ------ -------- ------- -------- -------- -----
total      204   70.66   367.91  259439  1446601        0   100

Elapsed times include waiting on following events:
  Event waited on               Times   Max. Wait  Total Waited
  ---------------------------- Waited  ----------  ------------
  read by other session         20820        0.24        133.64
  db file scattered read         8942        0.99        100.00 
  latch: cache buffers chains     193        0.02          0.73
  db file sequential read         302        0.07          1.39
Con filesystemio_options=setall las lecturas aleatorias mejoraron en 26%, mientras que los full table scans empeoraron en casi 58%!

Conclusiones

Revisando los resultados de los escenarios simulados, podemos concluir que, ante la activación de direct I/O, las aplicaciones intensivas en acceso aleatorio a los discos se ven beneficiadas mientras que las intensivas en full table scans se ven perjudicadas, de allí que no debemos saltar a la conclusión de que con solo habilitar el direct I/O nuestra base de datos será mágicamente más rápida, es posible que así sea, como también es posible que el impacto sea nulo e incluso adverso.

Primero hagan las pruebas del caso durante un periodo de carga típica antes de decidir dejarlo permanentemente. Lo que sí es seguro que mejorará el desempeño es el uso de filesystemio_options=asynch, por lo que es lo mínimo con lo que deberías configurar tu base de datos.

Cada plataforma tiene sus particularidades en cuanto a la aplicabilidad de los diversos valores para filesystemio_options e incluso hay algunos bugs específicos para ciertas combinaciones de versión de Oracle + versión de sistema operativo, por ello es conveniente que te informes más del tema.

Te recomiendo la lectura de Boost application performance using asynchronous I/O, y de las notas:

462072.1File System's Buffer Cache versus Direct I/O
555601.1How To Verify Whether DIRECTIO is Being Used
237299.1How To Check if Asynchronous I/O is Working On Linux
432854.1Asynchronous I/O Support on OCFS/OCFS2 and Related Settings: filesystemio_options, disk_asynch_io
¿Te pareció interesante este artículo?, ¿te quedaron algunas dudas?, ¿quieres sugerirme un tema a tratar?, pues déjame tus comentarios o ¡contáctame ahora mismo!

Agregue un comentario

Su dirección de correo no se hará público. Los campos requeridos están marcados *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Posts Relacionados

Primer post de una serie dedicada al arte de parchar Oracle. Empezamos con el parchado in-place, la forma más común y también la más peligrosa.
Aprenda a descargar los parches de Oracle, tanto manualmente como de forma automatizada, usando el utilitario getMOSPatch.
Link a articulo publicado en Toad World, sobre como aplicar un patch out-of-place a Grid Infrastructure, usando un Golden Image.

¿Necesitas Ayuda?

Completa estos datos y estaré en contacto a la brevedad.