Qcache_free_memory non è ancora pieno Ricevo un sacco di Qcache_lowmem_prunes


11

Ho appena iniziato a dilettarmi con la cache delle query per il nostro CMS.

Qualcuno può dirmi (o almeno dare una buona congettura) il motivo per cui ho un sacco di Qcache_lowmem_prunescui più della metà di Qcache_free_memoryè libero?

query_cache_size=512M
query_cache_limit=1M

Ecco come appare dopo circa 12 ore

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    | 
+-------------------------+-----------+

Questo è come si è preso cura di 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     | 
+-------------------------+-----------+

Risposte:


21

La cache delle query è una funzionalità molto utile, ma non essere tentato di prestargli troppa attenzione e non essere tentato di renderlo troppo grande. Comprendere alcuni dei suoi interni probabilmente aiuterà in tal senso.

La cache delle query inizia come un grosso pezzo contiguo di memoria disponibile. Quindi i "blocchi" vengono ricavati da questo grande blocco:

  • ogni query memorizzata nella cache richiede un blocco
  • il set di risultati associato prende un blocco
  • ogni tabella a cui fa riferimento una query memorizzata nella cache (indipendentemente dal numero di query che fanno riferimento a tale tabella nella cache) accetta anche un blocco, uno per tabella.

La dimensione del blocco è dinamica, ma il server alloca un minimo di query_cache_min_res_unitbyte per blocco, con un valore predefinito tipico di 4096 byte.

Ogni volta che le query, i loro risultati di accompagnamento e i riferimenti alle tabelle vengono rimossi dalla cache, sia invalidati dalla modifica delle tabelle sottostanti o dalla potatura per fare spazio a query più recenti, questo lascia nuovi buchi delle dimensioni per quanto grandi fossero quei blocchi e il numero di "blocchi liberi" di solito aumenta ... anche se se vengono liberati due o più blocchi contigui, il numero di "blocchi liberi" aumenta solo di 1 e i "blocchi liberi" non aumenteranno affatto se il nuovo- i blocchi liberati sono contigui a un blocco già libero: la dimensione di quel blocco libero diventa appena più grande. Qualsiasi blocco aperto di memoria libera nella cache delle query viene conteggiato come 1 blocco libero.

Naturalmente, un blocco libero più piccolo di query_cache_min_res_unitnon verrà utilizzato affatto.

Quindi, i frammenti della cache delle query. Se il server vuole memorizzare nella cache una nuova query e non è possibile organizzare blocchi liberi di dimensioni sufficienti (tale descrizione è ingannevolmente semplice, poiché l'algoritmo sottostante è complicato), qualcos'altro deve essere eliminato ... quello è tuo Qcache_lowmem_prunes. C'è un algoritmo "meno usato di recente" (LRU) che decide cosa viene potato.

Sarebbe sensato chiedersi perché il server non deframmenta la memoria ... ma non avrebbe senso. La cache delle query aiuta quando può ma non è affatto strategica. Non si desidera investire tempo di elaborazione (in particolare il tempo trascorso in un blocco globale) con attività di manutenzione non necessarie.

Sarebbe controproducente per il server dedicare tempo a riordinare - deframmentare - la memoria nella cache delle query, poiché i risultati memorizzati nella cache cambiano costantemente e l'intero punto della cache è migliorare le prestazioni.

Il blocco globale è un ottimo motivo per cui non si desidera utilizzare una cache di query eccessivamente grande ... il server trascorrerà troppo tempo lì poiché le query attendono il loro turno per vedere se si trovano nella cache e le prestazioni ne risentiranno .

Ma qcache_free_blocksè essenzialmente un indicatore della frammentazione dello spazio libero. Questo è ora molti blocchi non contigui di memoria disponibile nella cache delle query. Affinché una nuova query venga inserita nella cache, deve esserci una porzione abbastanza grande di spazio libero per contenere la query, i suoi risultati e (a volte) i suoi riferimenti alla tabella. Se non c'è, allora qualcos'altro deve andare ... che è quello che stai vedendo. Si noti, ancora una volta, che lo spazio disponibile non deve sempre essere contiguo (da quello che posso dire leggendo il codice sorgente), ma non tutti i buchi saranno riempiti quando c'è frammentazione.

Ma la frammentazione tende a livellarsi nel tempo, per un determinato carico di lavoro, dal momento che in genere nulla rimane nella cache delle query per tutto il tempo previsto.

Questo perché, in un certo senso, la cache delle query è brillante nella sua semplicità.

Ogni volta che i dati in una tabella a cui fa riferimento una query memorizzata nella cache cambiano, tutte le query che coinvolgono quella tabella vengono rimosse dalla cache, anche se la modifica non influisce sui risultati memorizzati nella cache. Questo è vero anche se una tabella cambia, ma non cambia, come nel caso di una transazione InnoDB che viene ripristinata. Le voci della cache della query che fanno riferimento a quella tabella sono già state eliminate.

Inoltre, la cache delle query viene controllata per ogni query in arrivo prima che il server analizzi effettivamente la query. L'unica cosa che corrisponderà è un'altra query esattamente uguale, byte per byte. SELECT * FROM my_tablee select * from my_tablenon sono identici byte per byte, quindi la cache della query non si rende conto che sono la stessa query.

FLUSH QUERY CACHEnon svuota la cache delle query. Deframmenta la cache delle query, motivo per cui Qcache_free_blocksdiventa "1" Tutto lo spazio libero è consolidato.

RESET QUERY CACHE effettivamente svuota (cancella tutto il contenuto di) la cache della query.

FLUSH STATUScancella i contatori, ma questo non è qualcosa che si desidera fare regolarmente perché azzera la maggior parte delle variabili di stato SHOW STATUS.

Ecco alcune brevi dimostrazioni.

Baseline:

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        |
+-------------------------+----------+

Esegui una query ...

mysql> select * from junk where id = 2;

Il numero totale di blocchi è aumentato di 3, gli inserimenti di 1 e le query nella cache sono 1.

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

Esegui la stessa query, ma con lettere maiuscole diverse ...

mysql> SELECT * FROM junk where id = 2;

Questa query è stata memorizzata nella cache separatamente. I blocchi totali sono aumentati solo di 2 perché era già stato assegnato un blocco per la tabella.

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

Ora, cambiamo una riga diversa nella tabella.

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

Sia le query che il riferimento alla tabella vengono invalidati dalla cache, lasciandoci con 1 blocco libero contiguo, tutta la memoria cache liberata e tutto lo spazio libero consolidato in un blocco.

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

MySQL non memorizzerà una query nella cache che non è deterministica, come SELECT NOW();o qualsiasi altra query a cui si dice specificamente di non memorizzare nella cache. SELECT SQL_NO_CACHE ...è la direttiva per dire al server di non archiviare i risultati nella cache. È utile per il benchmark del vero tempo di esecuzione di una query quando la cache fornisce una risposta ingannevolmente veloce sulle esecuzioni successive.


Nei tuoi esempi, è corretto che query_cache_min_res_unit = 512? la memoria libera scende di 512 * 3 tra 1 e 4 blocchi usati e 512 * 2 tra 4 e 6 blocchi usati.
aland

1
@aland questo è un ottimo punto. No, avrei dovuto usare il valore predefinito di 4096. Sembra che la cache della query riduca il blocco alla minima potenza possibile di due dopo averlo riempito, lasciando lo spazio libero alla fine, in modo che 7/8 di interi 4096 byte originariamente assegnati non sono bloccati. Dovrò scavare più a fondo in questo.
Michael - sqlbot,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.