Qcache_free_memory noch nicht voll Ich bekomme eine Menge Qcache_lowmem_prunes

11

Ich habe gerade angefangen, mich mit dem Abfrage-Cache für unser CMS zu beschäftigen.

Kann mir jemand sagen (oder zumindest eine gute Vermutung geben) , warum ich eine Menge von , Qcache_lowmem_pruneswenn mehr als die Hälfte Qcache_free_memoryist frei?

query_cache_size=512M
query_cache_limit=1M

So sieht es nach ca. 12 Stunden aus

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

So sah es aus 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     | 
+-------------------------+-----------+
Nifle
quelle

Antworten:

21

Der Abfrage-Cache ist eine sehr nette Funktion, aber seien Sie nicht versucht, ihm zu viel Aufmerksamkeit zu schenken, und seien Sie nicht versucht, ihn zu groß zu machen. Das Verständnis einiger seiner Interna wird wahrscheinlich in dieser Hinsicht helfen.

Der Abfragecache beginnt als ein großer zusammenhängender Teil des verfügbaren Speichers. Dann werden "Blöcke" aus diesem großen Block herausgeschnitten:

  • Jede zwischengespeicherte Abfrage benötigt einen Block
  • Die zugehörige Ergebnismenge nimmt einen Block
  • Jede Tabelle, auf die von einer zwischengespeicherten Abfrage verwiesen wird (unabhängig davon, wie viele Abfragen, die auf diese Tabelle verweisen, sich im Cache befinden), enthält ebenfalls einen Block, einen pro Tabelle.

Die query_cache_min_res_unitBlockgröße ist dynamisch, aber der Server weist ein Minimum von Bytes pro Block zu, mit einem typischen Standardwert von 4096 Bytes.

Jedes Mal, wenn Abfragen, die zugehörigen Ergebnisse und Tabellenreferenzen aus dem Cache entfernt werden, indem sie entweder durch Ändern der zugrunde liegenden Tabellen ungültig werden oder durch Bereinigen, um Platz für neuere Abfragen zu schaffen, bleiben neue Löcher in der Größe dieser Blöcke übrig Die Anzahl der "freien Blöcke" nimmt normalerweise zu. Wenn jedoch zwei oder mehr zusammenhängende Blöcke freigegeben werden, erhöht sich die Anzahl der "freien Blöcke" nur um 1, und die "freien Blöcke" erhöhen sich überhaupt nicht, wenn die neuen Blöcke freigegeben werden. Freigegebene Blöcke grenzen an einen bereits freien Block an - die Größe dieses freien Blocks wird nur größer. Jeder offene Block freien Speichers im Abfragecache wird als 1 freier Block gezählt.

Natürlich wird ein freier Block, der kleiner als query_cache_min_res_unitist, überhaupt nicht verwendet.

Also die Abfrage-Cache-Fragmente. Wenn der Server eine neue Abfrage zwischenspeichern möchte und keine freien Blöcke mit ausreichender Größe angeordnet werden können (diese Beschreibung ist täuschend einfach, da der zugrunde liegende Algorithmus kompliziert ist), muss etwas anderes entfernt werden ... das ist Ihre Qcache_lowmem_prunes. Es gibt einen "am wenigsten verwendeten" (LRU) Algorithmus, der entscheidet, was beschnitten wird.

Es wäre sinnvoll zu fragen, warum der Server den Speicher nicht defragmentiert ... aber das wäre nicht sinnvoll. Der Abfrage-Cache hilft, wenn es geht, ist aber überhaupt nicht strategisch. Sie möchten keine Verarbeitungszeit (insbesondere keine Zeit in einer globalen Sperre) in unnötige Wartungsaufgaben investieren.

Es wäre kontraproduktiv für den Server, Zeit damit zu verbringen, den Speicher im Abfragecache neu zu ordnen - zu defragmentieren -, da sich die zwischengespeicherten Ergebnisse ständig ändern und der gesamte Sinn des Caches darin besteht, die Leistung zu verbessern.

Die globale Sperre ist ein sehr guter Grund, warum Sie keinen übermäßig großen Abfrage-Cache verwenden möchten. Der Server verbringt dort zu viel Zeit, da Abfragen warten, bis sie an der Reihe sind, um festzustellen, ob sie zwischengespeichert werden und Ihre Leistung darunter leidet .

Dies qcache_free_blocksist jedoch im Wesentlichen ein Indikator für die Fragmentierung des freien Raums. Das sind jetzt viele nicht zusammenhängende Blöcke des verfügbaren Speichers im Abfragecache. Damit eine neue Abfrage in den Cache eingefügt werden kann, muss ein ausreichend großer Teil des freien Speicherplatzes vorhanden sein, um die Abfrage, ihre Ergebnisse und (manchmal) ihre Tabellenreferenzen zu enthalten. Wenn nicht, muss etwas anderes gehen ... was Sie sehen. Beachten Sie erneut, dass der verfügbare Speicherplatz nicht immer zusammenhängend sein muss (was ich durch Lesen des Quellcodes erkennen kann), aber nicht jedes Loch wird gefüllt, wenn es eine Fragmentierung gibt.

Die Fragmentierung nimmt jedoch bei einer bestimmten Arbeitslast im Laufe der Zeit ab, da normalerweise nichts so lange im Abfragecache verbleibt, wie Sie es erwarten.

Dies liegt daran, dass der Abfragecache in gewisser Weise in seiner Einfachheit brillant ist.

Jedes Mal, wenn sich die Daten in einer Tabelle ändern, auf die von einer zwischengespeicherten Abfrage verwiesen wird, werden alle Abfragen, die diese Tabelle betreffen, aus dem Cache entfernt - auch wenn sich die Änderung nicht auf die zwischengespeicherten Ergebnisse auswirkt. Dies gilt sogar, wenn sich eine Tabelle ändert, aber nicht ändert, wie im Fall einer InnoDB-Transaktion, die zurückgesetzt wird. Die Abfrage-Cache-Einträge, die auf diese Tabelle verweisen, wurden bereits gelöscht.

Außerdem wird der Abfragecache für jede eingehende Abfrage überprüft, bevor der Server die Abfrage tatsächlich analysiert. Das einzige, was übereinstimmt, ist eine andere Abfrage, die genau dieselbe war, Byte für Byte. SELECT * FROM my_tableund select * from my_tablesind nicht Byte für Byte identisch, sodass der Abfragecache nicht erkennt, dass es sich um dieselbe Abfrage handelt.

FLUSH QUERY CACHELeert den Abfragecache nicht. Es defragmentiert den Abfragecache, weshalb Qcache_free_blocks"1" wird. Der gesamte freie Speicherplatz wird konsolidiert.

RESET QUERY CACHE Leert den gesamten Abfrage-Cache (löscht den gesamten Inhalt).

FLUSH STATUSlöscht die Zähler, aber dies ist nicht etwas, das Sie routinemäßig tun möchten, da dadurch die meisten Statusvariablen auf Null gesetzt werden SHOW STATUS.

Hier sind einige kurze Demonstrationen.

Grundlinie:

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

Führen Sie eine Abfrage aus ...

mysql> select * from junk where id = 2;

Die Gesamtzahl der Blöcke wurde um 3 erhöht, die Einfügungen um 1 und die Anzahl der Abfragen im Cache 1.

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

Führen Sie dieselbe Abfrage aus, jedoch mit unterschiedlicher Großschreibung ...

mysql> SELECT * FROM junk where id = 2;

Diese Abfrage wurde separat zwischengespeichert. Die Gesamtzahl der Blöcke wurde nur um 2 erhöht, da wir bereits einen Block für die Tabelle zugewiesen hatten.

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

Jetzt ändern wir eine andere Zeile in der Tabelle.

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

Beide Abfragen und die Tabellenreferenz werden aus dem Cache ungültig gemacht, sodass 1 zusammenhängender freier Block, der gesamte freigegebene Cache-Speicher und der gesamte in einem Block konsolidierte freie Speicherplatz übrig bleiben.

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

MySQL speichert keine Abfrage im Cache, die nicht deterministisch ist - wie z. B. SELECT NOW();eine Abfrage, die Sie ausdrücklich anweisen, nicht zwischenzuspeichern. SELECT SQL_NO_CACHE ...ist die Anweisung, den Server anzuweisen, die Ergebnisse nicht im Cache zu speichern. Dies ist nützlich, um die tatsächliche Ausführungszeit einer Abfrage zu vergleichen, wenn der Cache auf nachfolgende Ausführungen eine täuschend schnelle Antwort liefert.

Michael - sqlbot
quelle
Ist es in Ihren Beispielen richtig, dass query_cache_min_res_unit = 512 ist? Der freie Speicher verringert sich zwischen 1 und 4 verwendeten Blöcken um 512 * 3 und zwischen 4 und 6 verwendeten Blöcken um 512 * 2.
Aland
1
@aland das ist ein sehr guter Punkt. Nein, ich hätte den Standardwert 4096 verwenden sollen. Es sieht so aus, als würde der Abfrage-Cache den Block nach dem Füllen auf die kleinstmögliche Zweierpotenz reduzieren und den freien Speicherplatz am Ende belassen, sodass 7/8 der Die gesamten ursprünglich zugewiesenen 4096 Bytes sind nicht gestrandet. Ich muss mich eingehender damit befassen.
Michael - sqlbot