Qcache_free_memory no está lleno todavía recibo muchas Qcache_lowmem_prunes


11

Acabo de comenzar a incursionar con el caché de consultas para nuestro CMS.

¿Puede alguien decirme (o por lo menos dar una buena suposición) por qué me sale un montón de Qcache_lowmem_prunescuando más de la mitad de Qcache_free_memoryes libre?

query_cache_size=512M
query_cache_limit=1M

Así es como se ve después de aproximadamente 12 horas

show status like '%qcach%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| Qcache_free_blocks      | 10338     | 
| Qcache_free_memory      | 297348320 | 
| Qcache_hits             | 10254104  | 
| Qcache_inserts          | 6072945   | 
| Qcache_lowmem_prunes    | 725279    | 
| Qcache_not_cached       | 2237603   | 
| Qcache_queries_in_cache | 48119     | 
| Qcache_total_blocks     | 111346    | 
+-------------------------+-----------+

Así es como se cuidaba flush query cache;

show status like '%qcach%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| Qcache_free_blocks      | 1         | 
| Qcache_free_memory      | 443559256 | 
| Qcache_hits             | 10307015  | 
| Qcache_inserts          | 6115890   | 
| Qcache_lowmem_prunes    | 725279    | 
| Qcache_not_cached       | 2249405   | 
| Qcache_queries_in_cache | 26455     | 
| Qcache_total_blocks     | 54490     | 
+-------------------------+-----------+

Respuestas:


21

El caché de consultas es una característica muy agradable, pero no caigas en la tentación de prestarle demasiada atención y no seas tentado a hacerlo demasiado grande. Comprender algunos de sus aspectos internos probablemente ayudará en ese sentido.

La caché de consultas comienza como una gran porción contigua de memoria disponible. Luego, los "bloques" están tallados en este gran bloque:

  • cada consulta en caché toma un bloque
  • su conjunto de resultados complementario toma un bloque
  • cada tabla a la que hace referencia cualquier consulta en caché (no importa cuántas consultas que hagan referencia a esa tabla en la caché) también toma un bloque, uno por tabla.

El tamaño del bloque es dinámico, pero el servidor asigna un mínimo de query_cache_min_res_unitbytes por bloque, con un valor predeterminado típico de 4096 bytes.

En cualquier momento, las consultas, los resultados que lo acompañan y las referencias de la tabla se eliminan de la memoria caché, ya sea al invalidarse por el cambio de las tablas subyacentes o al podar para dejar espacio para consultas más nuevas, esto deja nuevos agujeros del tamaño de los bloques que fueron, y el número de "bloques libres" generalmente aumenta ... aunque si se liberan dos o más bloques contiguos, el número de "bloques libres" solo aumenta en 1, y los "bloques libres" no aumentarán en absoluto si el Los bloques liberados son contiguos con un bloque ya libre: el tamaño de ese bloque libre solo se hace más grande. Cualquier bloque abierto de memoria libre en el caché de consultas se cuenta como 1 bloque libre.

Por supuesto, un bloque libre más pequeño que query_cache_min_res_unitno se utilizará en absoluto.

Entonces, la consulta almacena en caché los fragmentos. Si el servidor quiere almacenar en caché una nueva consulta y no se pueden organizar bloques libres de tamaño suficiente (esa descripción es engañosamente simple, porque el algoritmo subyacente es complicado), hay que podar otra cosa ... esa es su Qcache_lowmem_prunes. Hay un algoritmo de "uso menos reciente" (LRU) que decide qué se poda.

Sería sensato preguntar por qué el servidor no desfragmenta la memoria ... pero eso no tendría sentido. La caché de consultas ayuda cuando puede pero no es en absoluto estratégica. No desea invertir tiempo de procesamiento (especialmente el tiempo dedicado a un bloqueo global) con tareas de mantenimiento innecesarias.

Sería contraproducente para el servidor pasar tiempo reorganizando, desfragmentando, la memoria en la memoria caché de consultas, ya que los resultados almacenados en caché cambian constantemente y el objetivo de la memoria caché es mejorar el rendimiento.

El bloqueo global es una muy buena razón por la que no desea utilizar un caché de consultas excesivamente grande ... el servidor pasará demasiado tiempo allí ya que las consultas esperan su turno para ver si están en caché y su rendimiento se verá afectado .

Pero el qcache_free_blockses esencialmente un indicador de fragmentación de espacio libre. Ahora hay muchos bloques no contiguos de memoria disponible en la memoria caché de consultas. Para que una nueva consulta se inserte en la memoria caché, debe haber un espacio libre lo suficientemente grande como para contener la consulta, sus resultados y (a veces) sus referencias de tabla. Si no hay, entonces algo más tiene que ir ... que es lo que estás viendo. Tenga en cuenta, nuevamente, que el espacio disponible no siempre tiene que ser necesariamente contiguo (por lo que puedo decir al leer el código fuente), pero no todos los agujeros se llenarán cuando haya fragmentación.

Pero la fragmentación tiende a nivelarse con el tiempo, para una carga de trabajo dada, ya que generalmente nada permanece en la memoria caché de consultas durante el tiempo que cabría esperar.

Esto se debe a que, de alguna manera, la caché de consultas es brillante en su simplicidad.

Cada vez que los datos en una tabla referenciada por una consulta en caché cambian, todas las consultas que involucraron esa tabla se eliminan de la caché, incluso si el cambio no afecta los resultados en caché. Esto es incluso cierto si una tabla cambia, pero no cambia, como en el caso de una transacción InnoDB que se revierte. Las entradas de la memoria caché de consulta que hacen referencia a esa tabla ya se depuraron.

Además, el caché de consultas se verifica para cada consulta entrante antes de que el servidor realmente analice la consulta. Lo único que coincidirá es otra consulta que fue exactamente la misma, byte por byte. SELECT * FROM my_tabley select * from my_tableno son idénticos byte por byte, por lo que el caché de consultas no se da cuenta de que son la misma consulta.

FLUSH QUERY CACHEno vacía el caché de consultas. Desfragmenta el caché de consultas, por lo que se Qcache_free_blocksconvierte en "1". Todo el espacio libre está consolidado.

RESET QUERY CACHE en realidad vacía (borra todo el contenido de) el caché de consultas.

FLUSH STATUSborra los contadores, pero esto no es algo que desee hacer de forma rutinaria porque esto pone a cero la mayoría de las variables de estado SHOW STATUS.

Aquí hay algunas demostraciones rápidas.

Base:

mysql> show status like '%qcache%';
+-------------------------+----------+
| Variable_name           | Value    |
+-------------------------+----------+
| Qcache_free_blocks      | 1        |
| Qcache_free_memory      | 67091120 |
| Qcache_hits             | 0        |
| Qcache_inserts          | 0        |
| Qcache_lowmem_prunes    | 0        |
| Qcache_not_cached       | 1        |
| Qcache_queries_in_cache | 0        |
| Qcache_total_blocks     | 1        |
+-------------------------+----------+

Ejecute una consulta ...

mysql> select * from junk where id = 2;

El total de bloques aumentó en 3, las inserciones en 1 y las consultas en caché es 1.

+-------------------------+----------+
| Variable_name           | Value    |
+-------------------------+----------+
| Qcache_free_blocks      | 1        |
| Qcache_free_memory      | 67089584 |
| Qcache_inserts          | 1        |
| Qcache_queries_in_cache | 1        |
| Qcache_total_blocks     | 4        |
+-------------------------+----------+

Ejecute la misma consulta, pero con diferentes mayúsculas ...

mysql> SELECT * FROM junk where id = 2;

Esta consulta se almacenó en caché por separado. Los bloques totales solo aumentaron en 2 porque ya teníamos un bloque asignado para la tabla.

+-------------------------+----------+
| Variable_name           | Value    |
+-------------------------+----------+
| Qcache_free_blocks      | 1        |
| Qcache_free_memory      | 67088560 |
| Qcache_inserts          | 2        |
| Qcache_queries_in_cache | 2        |
| Qcache_total_blocks     | 6        |
+-------------------------+----------+

Ahora, cambiamos una fila diferente en la tabla.

mysql> update junk set things = 'items' where id = 1;

Ambas consultas y la referencia de la tabla se invalidan de la memoria caché, dejándonos con 1 bloque libre contiguo, toda la memoria caché liberada y todo el espacio libre consolidado en un bloque.

+-------------------------+----------+
| Variable_name           | Value    |
+-------------------------+----------+
| Qcache_free_blocks      | 1        |
| Qcache_free_memory      | 67091120 |
| Qcache_queries_in_cache | 0        |
| Qcache_total_blocks     | 1        |
+-------------------------+----------+

MySQL no almacenará una consulta en la memoria caché que no sea determinista, como SELECT NOW();cualquier consulta que le indique específicamente que no almacene en la memoria caché. SELECT SQL_NO_CACHE ...es la directiva para decirle al servidor que no almacene los resultados en la memoria caché. Es útil para comparar el verdadero tiempo de ejecución de una consulta cuando el caché le da una respuesta engañosamente rápida en ejecuciones posteriores.


En sus ejemplos, ¿es correcto que query_cache_min_res_unit = 512? la memoria libre disminuye 512 * 3 entre 1 y 4 bloques usados ​​y 512 * 2 entre 4 y 6 bloques usados.
aland

1
@aland que es un muy buen punto. No, debería haber estado usando el valor predeterminado de 4096. Parece que el caché de consultas recorta el bloque a la potencia más pequeña posible de dos después de llenarlo, dejando el espacio libre al final, de modo que 7/8 de la 4096 bytes completos originalmente asignados no están varados. Tendré que profundizar más en esto.
Michael - sqlbot
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.