Qcache_free_memory nie jest jeszcze pełna Dostaję dużo Qcache_lowmem_prunes

11

Właśnie zacząłem bawić się pamięcią podręczną zapytań dla naszego CMS.

Może ktoś mi powiedzieć (albo przynajmniej dać dobre przypuszczenie) dlaczego mam dużo z Qcache_lowmem_prunesgdy więcej niż połowa Qcache_free_memoryjest za darmo?

query_cache_size=512M
query_cache_limit=1M

Tak to wygląda po około 12 godzinach

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

Tak to wyglądało 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
źródło

Odpowiedzi:

21

Pamięć podręczna zapytań jest bardzo miłą funkcją, ale nie kusz się, aby zwracać na nią zbyt dużą uwagę i nie kusi, aby była zbyt duża. Zrozumienie niektórych jego elementów wewnętrznych prawdopodobnie pomoże w tym zakresie.

Pamięć podręczna zapytań rozpoczyna się jako jeden duży ciągły fragment dostępnej pamięci. Następnie „bloki” są wycinane z tego dużego bloku:

  • każde zapytanie buforowane zajmuje blok
  • jego zestaw wyników towarzyszących zajmuje blok
  • każda tabela, do której odwołuje się dowolne zapytanie buforowane (bez względu na to, ile zapytań odnoszących się do tej tabeli znajduje się w pamięci podręcznej), również przyjmuje blok, po jednym na tabelę.

Rozmiar bloku jest dynamiczny, ale serwer przydziela minimalną liczbę query_cache_min_res_unitbajtów na blok, przy typowej wartości domyślnej wynoszącej 4096 bajtów.

Za każdym razem, gdy zapytania, towarzyszące im wyniki i odwołania do tabel są usuwane z pamięci podręcznej, albo przez unieważnienie przez zmianę tabel bazowych, albo przez przycinanie, aby zrobić miejsce dla nowych zapytań, pozostawia to nowe dziury wielkości, jakkolwiek duże były te bloki, i liczba „wolnych bloków” zwykle wzrasta ... chociaż jeśli dwa lub więcej sąsiadujących bloków zostanie zwolnionych, liczba „wolnych bloków” wzrośnie tylko o 1, a „wolne bloki” w ogóle nie wzrosną, jeśli nowo- uwolnione bloki sąsiadują z już wolnym blokiem - rozmiar tego wolnego bloku staje się po prostu większy. Każdy otwarty blok wolnej pamięci w pamięci podręcznej zapytania jest liczony jako 1 wolny blok.

Oczywiście, wolny blok mniejszy niż query_cache_min_res_unitnie będzie w ogóle używany.

Fragmenty pamięci podręcznej zapytania. Jeśli serwer chce buforować nowe zapytanie i nie można ustawić wolnych bloków o wystarczającej wielkości (ten opis jest zwodniczo prosty, ponieważ podstawowy algorytm jest skomplikowany), należy jeszcze przyciąć coś innego ... to twoje Qcache_lowmem_prunes. Istnieje algorytm „najrzadziej ostatnio używany” (LRU), który decyduje o tym, co zostanie przycięte.

Rozsądnie byłoby zapytać, dlaczego serwer nie defragmentuje pamięci ... ale to nie miałoby sensu. Pamięć podręczna zapytań pomaga, kiedy może, ale wcale nie jest strategiczna. Nie chcesz inwestować czasu przetwarzania (zwłaszcza czasu spędzonego w globalnej blokadzie) na niepotrzebne zadania konserwacyjne.

Niekorzystne byłoby, gdyby serwer spędzał czas na przestawianiu - defragmentacji - pamięci w pamięci podręcznej zapytań, ponieważ wyniki w pamięci podręcznej ciągle się zmieniają, a celem pamięci podręcznej jest poprawa wydajności.

Globalna blokada to bardzo dobry powód, dla którego nie chcesz używać zbyt dużej pamięci podręcznej zapytań ... serwer spędza tam zbyt dużo czasu, ponieważ zapytania czekają na swoją kolej, aby sprawdzić, czy zdarzy się, że zostaną buforowane, a wydajność spadnie .

Ale qcache_free_blocksjest to zasadniczo wskaźnik fragmentacji wolnej przestrzeni. To teraz wiele nieciągłych bloków dostępnej pamięci istnieje w pamięci podręcznej zapytań. Aby nowe zapytanie mogło zostać wstawione do pamięci podręcznej, musi być wystarczająco duży kawałek wolnego miejsca, aby pomieścić zapytanie, jego wyniki i (czasami) odwołania do tabeli. Jeśli nie ma, to musi iść coś innego ... co właśnie widzisz. Zauważ ponownie, że dostępna przestrzeń nie zawsze musi być ciągła (z tego, co mogę odczytać po przeczytaniu kodu źródłowego), ale nie każda dziura zostanie wypełniona, gdy nastąpi fragmentacja.

Ale fragmentacja ma tendencję do wyrównywania się w czasie dla danego obciążenia, ponieważ zwykle nic nie pozostaje w pamięci podręcznej zapytań tak długo, jak można się spodziewać.

Jest tak, ponieważ pod pewnymi względami pamięć podręczna zapytań jest genialna w swojej prostocie.

Za każdym razem, gdy zmieniają się dane w tabeli, do której odwołuje się zapytanie z pamięci podręcznej, wszystkie zapytania dotyczące tej tabeli są usuwane z pamięci podręcznej - nawet jeśli zmiana nie wpływa na wyniki w pamięci podręcznej. Dzieje się tak nawet wtedy, gdy tabela się zmienia, ale się nie zmienia, jak w przypadku wycofanej transakcji InnoDB. Pozycje pamięci podręcznej zapytań odnoszące się do tej tabeli zostały już usunięte.

Ponadto pamięć podręczna zapytania jest sprawdzana dla każdego przychodzącego zapytania, zanim serwer faktycznie go przeanalizuje. Jedyne, co pasuje, to kolejne zapytanie, które było dokładnie takie samo, bajt po bajcie. SELECT * FROM my_tablei select * from my_tablenie są identyczne bajt po bajcie, więc pamięć podręczna zapytań nie zdaje sobie sprawy, że to samo zapytanie.

FLUSH QUERY CACHEnie opróżnia pamięci podręcznej zapytań. Defragmentuje pamięć podręczną zapytań, dlatego Qcache_free_blocksstaje się „1”. Cała wolna przestrzeń jest skonsolidowana.

RESET QUERY CACHE faktycznie opróżnia (usuwa całą zawartość) pamięci podręcznej zapytań.

FLUSH STATUSkasuje liczniki, ale nie jest to rutynowe działanie, ponieważ powoduje to wyzerowanie większości zmiennych statusu w SHOW STATUS.

Oto kilka szybkich demonstracji.

Linia bazowa:

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

Uruchom zapytanie ...

mysql> select * from junk where id = 2;

Całkowita liczba bloków wzrosła o 3, wstawia o 1, a zapytania w pamięci podręcznej wynoszą 1.

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

Uruchom to samo zapytanie, ale z inną wielkością liter ...

mysql> SELECT * FROM junk where id = 2;

To zapytanie zostało buforowane osobno. Całkowita liczba bloków wzrosła tylko o 2, ponieważ już mieliśmy blok przydzielony do tabeli.

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

Teraz zmieniamy inny wiersz w tabeli.

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

Zarówno zapytania, jak i odwołanie do tabeli są unieważniane z pamięci podręcznej, pozostawiając nam 1 ciągły wolny blok, całą pamięć podręczną zwolnioną, a cała wolna przestrzeń skonsolidowana w jednym bloku.

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

MySQL nie będzie przechowywał zapytania w pamięci podręcznej, które nie jest deterministyczne - takiego jak SELECT NOW();każde zapytanie, którego nie chcesz buforować. SELECT SQL_NO_CACHE ...to dyrektywa mówi serwerowi, aby nie zapisywał wyników w pamięci podręcznej. Jest przydatny do testowania rzeczywistego czasu wykonania zapytania, gdy pamięć podręczna daje zwodniczo szybką odpowiedź na kolejne wykonania.

Michael - sqlbot
źródło
Czy w twoich przykładach jest poprawne, że query_cache_min_res_unit = 512? wolna pamięć spada o 512 * 3 między 1 a 4 zużytymi blokami i 512 * 2 między 4 a 6 zużytymi blokami.
i
1
@aland to bardzo dobry punkt. Nie, powinienem był użyć domyślnej wartości 4096. Wygląda na to, że pamięć podręczna zapytania przycina blok do najmniejszej możliwej potęgi dwóch po jego wypełnieniu, pozostawiając wolne miejsce na końcu, tak że 7 / 8th z całe 4096 bajtów pierwotnie przydzielonych nie jest splecionych. Będę musiał głębiej w to zagłębić.
Michael - sqlbot