Bienvenidos sean a este post, hoy hablaremos sobre los distintos caches para InnoDB.
Hasta ahora hemos hablado de que InnoDB es el engine que se recomienda usar para la mayoria de los casos, por lo tanto debemos saber como configurarlo, el pool de buffer de InnoDB es un cache que deberia acelerear las operaciones de lectura y escritura, en cambio el buffer de doble escritura es un mecanismo importante que garantiza que ninguna fila se escribira por la mitad en el archivo, aunque para cargas de trabajo muy pesadas podemos desactivarlos en caso de ser necesario, comencemos con los distintos tipos.
Paginas de InnoDB
Las tablas, datos e indices son organizadas en paginas, ambas en caches y en archivos, pero una pagina es una paquete de datos que contiene una o dos filas y algun espacio vacio, el ratio entre el espacio usado y el tamaño total de las paginas es llamado Factor de Llenado (Fill Factor).
Las columnas de tipo tamaño variable (TEXT, BLOB, VARHCAR o VARBIT) son escritas dentro de estructura de datos separadas llamdas paginas de desbordamiento (overlow pages), tambien se las denomina paginas fuera de la columnas, a su vez el que mejor las maneja es el formato de fila DYNAMIC, las cuales pueden ser usadas cuando la retrocompatibilidad no nos interesa, por ultimo una pagina nunca cambia su tamaño y el tamaño es igual para todas las paginas, aunque si podemos configurar el tamaño del mismo y tenemos entre 4 kB, 8 kB y 16 kB siendo este ultimo el predeterminado, si bien este tamaño es el ideal para muchas tareas para algunos trabajos de tipo OTLP nos conviene mas usar el tamaño mas chico, o para que trabaje mejor con algunos dispositivos viejos SSD (Disco de estado solido).
Nota: El tamaño se cambia modificando la variable innodb_page_size en el archivo de configuraciones.
Pool de Buffer de InnoDB
Este es el cache mas importante a considerar para ser utilizado en servidores, lo ideal seria que contenga todos los datos e indices para ejecutar queries sin acceder al disco, los cambios se hacen en el buffer primero para ser bajados mas tarde y esto reduce los accesos al disco, si los datos no encajan en la memoria solo un sub conjunto de ellos pueden estar en este buffer, tambien denominado conjunto de trabajo (working set) que no son otra cosas que los datos mas accedidos, el tamaño predeterminado es de 128 MB y siempre deberia ser cambiado, dado que para servidores es un valor muy bajo pero para un desarrollador con solo 5 MB es mas que necesario, por eso pueden variarlo dependiendo de la necesidad.
Con todo esto podemos pensar que este buffer es una lista de paginas de datos con una variacion del clasico algoritmo de Ultimo Recientemente Usado (LRU por sus siglas en ingles), la lista se divide en dos sub listas donde la nueva lista contiene las paginas mas usadas y la vieja contiene las paginas menos usadas, la primera pagina en cada sublista es llamada cabeza (head) y la primera de la lista vieja es llamada punto medio (midpoint), cuando una pagina es accedida y esta no se encuentra en el buffer se la agrega al midpoint, esto cambia todas las posiciones de la lista vieja haciendo que la ultima sea excluida, cuando una pagina de la lista vieja es accedida esta pasa de la lista vieja la cabeza de la lista nueva y cuando una pagina de la lista nueva es accedida pasa a la cabeza de la misma, veamos a las variables que afectan a este algoritmo:
- innodb_old_blocks_pct, define el porcentaje que reserva del pool de buffer para la lista vieja
- innodb_old_blocks_time, representa el tiempo minimo (en milisegundos) que una pagina vieja debe alcanzar para pasar a una lista nueva
- innodb_max_dirty_pages_pct, define el porcentaje maximo de paginas modificadas en memoria
- innodb_flush_neighbors, define la forma de como se descargara la informacion modificada en el buffer
Si tenemos una situacion donde la concurrencia es muy alta y nuestro buffer es muy grande podemos generar una situacion de cuello de botella por el bloqueo de threads concurrente a traves de un mutex, para evitar esto a partir de la version 5.5 se pueden crear multiples instancias de este buffer, veamos las variables que nos permiten controlar a las instancias:
- innodb_buffer_pool_size, define el tamaño total del buffer
- innodb_buffer_pool_instances, define el numero de instancias, si pasa -1 la cantidad es definida por el sistema
Cuando hablamos de paginas sucias (dirty pages) nos referimos a las paginas que son modificada en el buffer en cambio cuando no son modificadas o ya se escribieron en el disco se denomina paginas limpias (clean pages), recuerden que los cambios de datos tambien son escritos en el log redo, para recuperar los datos en caso de algun fallo, a partir de la version 10.0 para hacer la descarga de los datos se hace a traves de un thread dedicado llamado limpia paginas (page cleaner) en versiones anteriores se hacia con el thread master, pero recuerden que la descarga no solo involucra al buffer sino tambien a los logs de undo y redo, si bien estas paginas son constantemente actualizadas con las transacciones estas poseen un mutex que no bloquea todo el buffer, estas tambien son descargadas cuando se alcanza el valor maximo informado en innodb_max_dirty_pages_pct, por ultimo tenemos a la variable innodb_flush_neighbor_pages la cual determina como se descargaran las paginas y posee tres valores:
- none, solo paginas seleccionadas son escritas
- area, se escriben con las paginas sucias vecinas
- cont, todos los bloques continuos a las paginas sucias se descargan
Cuando se apaga el equipo para tener un apagado correcto sin inconvenientes se establece a la variable innodb_fast_shutdown con el valor de 0, esto hara que toda la informacion se guarde manera consistente pero si tenemos muchos trabajos pendientes este hara que el equipo se apague de forma mas lenta, podemos modificar a esta variable por un valor mas alto lo cual hara que el equipo se apague de forma mas rapida pero esto hara que se ejecuta una recuperacion del equipo en el proximo inicio.
Una caracteristica interesante es la Optimizacion de Lectura Anticipada (read ahead optimization) la cual esta diseñada para reducir el numero de operaciones de lectura del disco, intentando adivinar cual dato sera necesario en un futuro y la lee con una sola operacion, esta dispone de dos algoritmos:
- Lectura anticipada lineal
- Lectura anticipada al azar
El primero es usado de manera predeterminada y cuenta todas las paginas en el buffer que son leidas secuencialmente, si este numero es igual o mas grande que innodb_read_ahead_threshold se leera todos los datos de esta extension, este variable puede tener un valor entre 0 y 64 siendo desactivado con el valor 0 y su valor predeterminado es 56, el segundo algoritmo es utilizado si innodb_random_read_ahead tiene el valor ON, su valor predeterminado es OFF, trabaja chequeando si al menos 13 paginas han sido leido en la misma extension, para este caso no importa si fueron leidas secuencialmente, si se cumple se procede a leer esta extension, la cantidad de paginas no es configurable, si el primer algoritmo tiene el valor 0 no habilita automaticamente a este segundo, y por ultimo si el primer algoritmo tiene el valor 0 y este segundo tiene el valor OFF se desactiva completamente la optimizacion.
En la base de information_schema tenemos una tabla llamada INNODB_BUFFER_POOL_STATS la cual nos permite saber todas las estadisticas del buffer, veamos sus columnas:
- POOL_ID, el id de las instancias del buffer
- POOL_SIZE, la cantidad de paginas de la instancia
- FREE_BUFFERS, numero de paginas libres
- DATABASE_PAGES, numero total de paginas de datos
- OLD_DATABASE_PAGES, paginas de la lista vieja
- MODIFIED_DATABASE_PAGES, paginas sucias
- PENDING_DECOMPRESS, numero de paginas pendientes de descompresion
- PENDING_READS, operaciones pendiente de lecturas
- PENDING_FLUSH_LRU, paginas pendientes en las listas a ser descargadas
- PENDING_FLUSH_LIST, paginas en la lista de descarga que necesitan descargarse
- PAGES_MADE_YOUNG, numero de paginas movidas a la lista nueva
- PAGES_NOT_MADE_YOUNG, paginas viejas que no pasan a la lista nueva
- PAGES_MADE_YOUNG_RATE, paginas convertidas a nuevas por segundo
- PAGES_MADE_NOT_YOUNG_RATE, paginas leidas pero no convertidas a nuevas por segundo
- NUMBER_PAGES_READ, numero de paginas leidas desde el disco
- NUMBER_PAGES_CREATED, numero de paginas creadas en el buffer
- NUMBER_PAGES_WRITTEN, numero de paginas escritas en el disco
- PAGES_READ_RATE, paginas leidas desde el disco por segundo
- PAGES_CREATE_RATE, paginas creadas en el buffer por segundo
- PAGES_WRITTEN_RATE, paginas escritas al disco por segundo
- NUMBER_PAGES_GET, solicitudes de paginas que no estan en el buffer
- HIT_RATE, el ratio de coincidencia de paginas
- YOUNG_MAKE_PER_THOUSAND_GETS, paginas hechas nuevas por cientos de lecturas fisicas
- NOT_YOUNG_MAKE_PER_THOUSAND_GETS, paginas que queda en la lista vieja por cientos de lecturas
- NUMBER_PAGES_READ_AHEAD, numero de paginas leidas por la operacion de lectura anticipada
- NUMBER_READ_AHEAD_EVICTED, numero de paginas no leidas y luego desalojadas por la operacion de lectura anticipada
- READ_AHEAD_RATE, idem a NUMBER_PAGES_READ_AHEAD pero este es el ratio por segundos
- READ_AHEAD_EVICTED_RATE, idem a NUMBER_READ_AHEAD_EVICTED pero este es el ratio por segundos
- LRU_IO_TOTAL, numero total de paginas escritas o leidas al disco
- LRU_IO_CURRENT, idem al anterior pero en el ultimo segundo
- UNCOMPRESS_TOTAL, paginas que se han descomprimido
- UNCOMPRESS_CURRENT, idem al anterior pero en el ultimo segundo
Cuando el servidor esta detenido y es iniciado nuestra tabla de pool de buffer esta vacia y se necesita rellenar con todos los datos necesarios, este proceso se llama calentamiento (warm-up) y este proceso no termina hasta estar completo y puede afectar a la performance de nuestro equipo, para evitar lo que se hace usualmente es realizar un volcado de dicha tabla y se lo rellena a partir de esta, para habilitar esta forma de trabajar se utilizan dos variables:
- innodb_buffer_pool_dump_at_shutdown
- innodb_buffer_pool_load_at_startup
En este caso la primera es para indicar que haga el volcado antes de apagarlo y la segunda para indicarle que cargue el volcado al iniciar, para lograr esto se debe poner ambos valores a ON, su valor predeterminado es OFF, pero esto tiene truco:
- Esto puede hacer el apagado e inicio mas lentos, y bajo ciertas circunstancias necesitamos que inicie rapido sacrificando la performance del mismo
- Siempre debemos tener un espacio en el disco para realizar el volcado
Pero esto no esta restringido al apagado e inicio sino que tambien podemos hacerlo en cualquier momento, para ello podemos estas dos variables:
- innodb_buffer_pool_dump_now
- innodb_buffer_pool_load_now
La primera sera que la hace el volcado y la segunda es la que la carga, en ambos casos para activarlo debemos pasarle el valor ON y comenzara a hacerlo en el momento, si ven el contenido de las variables siempre devuelve el valor de OFF, tambien podemos saber el estado de los procesos disparados por las variables anteriores podemos usar las siguientes variables:
- Innodb_buffer_pool_dump_status
- Innodb_buffer_pool_load_status
Los cuales nos devolveran el estado de volcado y carga respectivamente, por ultimo tenemos la variable innodb_buffer_pool_filename que nos devolvera el path y el nombre del archivo donde se hizo el volcado.
El buffer de cambio de InnoDB
Este cache es parte del cache anterior, contiene paginas sucias relacionadas a indices secudarias (no indices primarios) que no estan almacenadas en la parte principal del pool de buffer, si los datos modificados son leidos despues son enviados en el pool de buffer, en versiones anteriores se llamaba buffer de insercion pero dado que permite borrar fue cambiado al nombre actual, este cache acelera las siguientes operaciones:
- insertions, cuando nuevas filas son escritas
- deletions, cuando las filas son marcadas para eliminacion pero no fisicamente por tema de performance
- purges, es la eliminacion fisica de las filas marcadas e indices obsoletos, esto se hace periodicamente con un thread dedicado
Veamos las variables que configuran a este cache:
- innodb_change_buffer_max_size, define el tamaño maximo del buffer de cambio, se lo expresa como un porcentaje, sus valores van de 0 a 50 y el valor predeterminado es 25
- innodb_change_buffering, determina cuales son los tipos de operaciones que seran almacenados en el cache
Pasemos a ver cuales son los valores permitidos en esta ultima variable:
- none
- all (predeterminado)
- inserts
- deletes
- purges
- changes
El buffer de doble escritura
Hasta aqui hemos visto las caches mas utilizadas pero al momento de escribir en el disco podemos tener dos fallas que pueden interrumpir la operacion
- Fallas de hardware
- Fallas del sistema operativo
Para la primera no tenemos forma de evitarla si se «rompio» el disco, pero para el segundo caso no deberia ser posible si las paginas no son mas grandes que los bloques del sistema, pero si estan pensando en los logs de redo y undo no van a ser suficientes para recuperar las paginas a medio escribir porque estos poseen las identificaciones y no los datos, entonces para solucionarlo entra en accion el cache del titulo.
Este mecanismo trabaja escribiendo dos veces todas las paginas, una pagina es valida solo despues que se escribio la segunda y cuando el servidor reinicia si ocurre una recuperacion las paginas a medio escribir se descartan y este buffer afectara poco en la performance porque las escrituras son secuenciales y se escriben todas juntas, aunque podemos desactivarlo estableciendo el valor OFF en la variable innodb_doublewrite en los archivos de configuracion o bien al inicio usando la opcion –skip-innodb-doublewrite, esto es recomendable si la exactitud de los datos no es importante y necesitamos la mejor performance de nuestro equipo dado que genera una sobrecarga de nuestro disco con tanta escritura pero si exactitud de los datos es lo principal no deben desactivarlo.
En resumen, hoy hemos visto los caches de InnoDB, cuales son, sus particularidades, para que se usan y como nos benefician los mismos, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
