Qcache_free_memory belum penuh saya mendapatkan banyak Qcache_lowmem_prunes

11

Saya baru saja mulai mencoba-coba dengan cache permintaan untuk CMS kami.

Bisa ada yang bilang saya (atau setidaknya memberikan yang baik menebak) mengapa saya mendapatkan banyak dari Qcache_lowmem_prunesketika lebih dari setengah dari Qcache_free_memoryyang gratis?

query_cache_size=512M
query_cache_limit=1M

Ini adalah tampilannya setelah sekitar 12 jam

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

Inilah yang terlihat setelah itu 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
sumber

Jawaban:

21

Cache permintaan adalah fitur yang sangat bagus, tetapi jangan tergoda untuk terlalu memperhatikannya dan jangan tergoda untuk membuatnya terlalu besar. Memahami beberapa internal mungkin akan membantu dalam hal itu.

Cache permintaan dimulai sebagai satu potongan besar dari memori yang tersedia. Lalu "balok" diukir dari balok besar ini:

  • setiap permintaan yang di-cache mengambil blok
  • resultset pendampingnya mengambil blok
  • setiap tabel direferensikan oleh kueri yang di-cache (tidak peduli berapa banyak kueri yang mereferensikan tabel itu dalam cache) juga membutuhkan satu blok, satu per tabel.

Ukuran blok adalah dinamis, tetapi server mengalokasikan minimum query_cache_min_res_unitbyte per blok, dengan standar khas 4096 byte.

Setiap permintaan waktu, hasil yang menyertainya, dan referensi tabel dihapus dari cache, baik dengan menjadi tidak valid oleh perubahan tabel yang mendasarinya atau dengan pemangkasan untuk memberikan ruang bagi permintaan yang lebih baru, ini membuat lubang baru ukuran seberapa besar blok itu, dan jumlah "blok bebas" biasanya meningkat ... walaupun jika dua atau lebih blok yang berdekatan dilepaskan, jumlah "blok bebas" hanya meningkat sebesar 1, dan "blok bebas" tidak akan bertambah sama sekali jika blok yang dibebaskan bersebelahan dengan blok yang sudah bebas - ukuran blok gratis itu menjadi lebih besar. Setiap blok memori bebas yang terbuka di cache kueri dihitung sebagai 1 blok gratis.

Tentu saja, blok gratis yang lebih kecil daripada query_cache_min_res_unittidak akan digunakan sama sekali.

Jadi, fragmen cache kueri. Jika server ingin men-cache permintaan baru dan tidak ada blok gratis dengan ukuran yang cukup dapat diatur (deskripsi itu tampak sederhana, karena algoritma yang mendasarinya rumit), sesuatu yang lain harus dipangkas ... itu Anda Qcache_lowmem_prunes. Ada algoritma "LRU) yang paling baru-baru ini digunakan yang memutuskan apa yang akan dipangkas.

Akan masuk akal untuk bertanya mengapa server tidak mendefrag memori ... tetapi itu tidak masuk akal. Cache permintaan membantu ketika bisa tetapi itu sama sekali tidak strategis. Anda tidak ingin menginvestasikan waktu pemrosesan (terutama waktu yang dihabiskan dalam kunci global) dengan tugas pemeliharaan yang tidak perlu.

Akan menjadi kontra-produktif bagi server untuk menghabiskan waktu menata ulang - defragmenting - memori dalam cache kueri, karena hasil cache terus-menerus berubah dan seluruh titik cache adalah untuk meningkatkan kinerja.

Kunci global adalah alasan yang sangat bagus mengapa Anda tidak ingin menggunakan cache permintaan yang terlalu besar ... server akan menghabiskan terlalu banyak waktu di sana karena pertanyaan menunggu giliran mereka untuk melihat apakah mereka di-cache dan kinerja Anda akan menderita .

Tetapi pada qcache_free_blocksdasarnya merupakan indikator fragmentasi ruang bebas. Itu sekarang banyak blok memori yang tidak berdampingan yang ada di cache kueri. Agar kueri baru dimasukkan ke dalam cache, harus ada ruang kosong yang cukup besar untuk memuat kueri, hasilnya, dan (kadang-kadang) referensi tabelnya. Jika tidak ada, maka sesuatu yang lain harus pergi ... yang Anda lihat. Perhatikan, sekali lagi, bahwa ruang yang tersedia tidak selalu harus bersebelahan (dari apa yang dapat saya katakan dengan membaca kode sumber), tetapi tidak setiap lubang akan diisi ketika ada fragmentasi.

Namun fragmentasi cenderung meningkat seiring berjalannya waktu, karena beban kerja yang diberikan, karena tidak ada yang biasanya tetap dalam cache kueri selama yang Anda harapkan.

Ini karena, dalam beberapa hal, cache permintaan brilian dalam kesederhanaannya.

Kapan saja data dalam tabel yang direferensikan oleh perubahan cache di-cache, semua kueri yang terkait dengan tabel tersebut dihapus dari cache - bahkan jika perubahan tidak berdampak pada hasil yang di-cache. Ini bahkan berlaku jika tabel berubah, tetapi tidak berubah, seperti dalam kasus transaksi InnoDB yang dibatalkan. Entri cache permintaan referensi tabel itu sudah dibersihkan.

Juga, cache permintaan diperiksa untuk setiap permintaan yang masuk sebelum server benar-benar mem-parsing permintaan. Satu-satunya hal yang akan cocok adalah kueri lain yang persis sama, byte-for-byte. SELECT * FROM my_tabledan select * from my_tabletidak identik byte-untuk-byte, sehingga cache kueri tidak menyadari bahwa mereka adalah query yang sama.

FLUSH QUERY CACHEtidak mengosongkan cache kueri. Ini mendefrag cache permintaan, yang mengapa Qcache_free_blocksmenjadi "1." Semua ruang kosong dikonsolidasikan.

RESET QUERY CACHE sebenarnya flushes (menghapus semua konten) cache permintaan.

FLUSH STATUSmembersihkan penghitung, tetapi ini bukan sesuatu yang ingin Anda lakukan secara rutin karena ini menghilangkan sebagian besar variabel status di SHOW STATUS.

Berikut ini beberapa demonstrasi cepat.

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

Jalankan kueri ...

mysql> select * from junk where id = 2;

Total blok meningkat sebesar 3, menyisipkan 1 dan kueri dalam cache adalah 1.

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

Jalankan kueri yang sama, tetapi dengan kapitalisasi berbeda ...

mysql> SELECT * FROM junk where id = 2;

Permintaan ini di-cache secara terpisah. Total blok hanya bertambah 2 karena kami sudah memiliki blok yang dialokasikan untuk tabel.

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

Sekarang, kita mengubah baris berbeda di tabel.

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

Baik kueri dan referensi tabel tidak valid dari cache, meninggalkan kami dengan 1 blok bebas yang berdekatan, semua memori cache dibebaskan, dan semua ruang kosong dikonsolidasikan dalam satu blok.

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

MySQL tidak akan menyimpan kueri dalam cache yang tidak deterministik - seperti SELECT NOW();atau kueri apa pun yang Anda katakan secara khusus untuk tidak cache. SELECT SQL_NO_CACHE ...adalah arahan untuk memberi tahu server untuk tidak menyimpan hasil dalam cache. Ini berguna untuk membuat tolok ukur waktu eksekusi sebenarnya dari sebuah kueri ketika cache memberi Anda respons yang sangat cepat pada eksekusi berikutnya.

Michael - sqlbot
sumber
Dalam contoh Anda, apakah benar query_cache_min_res_unit = 512? memori bebas turun sebesar 512 * 3 antara 1 dan 4 blok bekas dan 512 * 2 antara 4 dan 6 blok bekas.
aland
1
@aland itu adalah poin yang sangat bagus. Tidak, saya seharusnya menggunakan nilai default 4096. Sepertinya cache query memangkas blok ke kekuatan sekecil mungkin dari dua setelah mengisinya, meninggalkan ruang kosong di akhir, sehingga 7/8 dari Seluruh 4096 byte yang awalnya dialokasikan tidak terdampar. Saya harus menggali lebih dalam tentang ini.
Michael - sqlbot