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_unit
byte 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_unit
non 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_table
e select * from my_table
non sono identici byte per byte, quindi la cache della query non si rende conto che sono la stessa query.
FLUSH QUERY CACHE
non svuota la cache delle query. Deframmenta la cache delle query, motivo per cui Qcache_free_blocks
diventa "1" Tutto lo spazio libero è consolidato.
RESET QUERY CACHE
effettivamente svuota (cancella tutto il contenuto di) la cache della query.
FLUSH STATUS
cancella 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.