Posting blog ini dimotivasi oleh diskusi internal tentang cara menonaktifkan sepenuhnya cache kueri di MySQL
Menurut , kita seharusnya dapat menonaktifkan “Query Cache” dengan cepat dengan mengubah query_cache_type menjadi 0, tetapi seperti yang akan kita lakukan . Blog ini akan menunjukkan kepada Anda cara menonaktifkan "cache kueri" dengan benar, dan bagaimana praktik umum mungkin tidak sebaik yang kami kira.
Bisakah kita menonaktifkannya dengan mengubah variabel, atau memerlukan restart untuk menghindari mutex global?
Beberapa konteks Cache Kueri
Cache kueri menyimpan teks pernyataan "Pilih" bersama dengan hasil terkait yang dikirim ke klien. Jika pernyataan yang identik diterima nanti, server mengambil hasilnya dari cache kueri daripada mem-parsing dan mengeksekusi pernyataan itu lagi. Cache kueri dibagikan di antara sesi, sehingga kumpulan hasil yang dihasilkan oleh satu klien dapat dikirim sebagai tanggapan atas kueri yang sama yang dikeluarkan oleh klien lain
Namun, kueri yang dapat di-cache mengambil "kunci eksklusif" di cache kueri MySQL. Selain itu, penyisipan, pemutakhiran, penghapusan, atau modifikasi lain apa pun pada tabel menyebabkan entri apa pun yang relevan dalam cache kueri dihapus. Jika Anda melihat banyak "Menunggu kunci cache kueri" di daftar proses, Anda mungkin menderita kunci eksklusif ini. Dalam posting blog ini, Anda dapat melihat bagaimana mutex global dalam konkurensi tinggi ini dapat menyebabkan penurunan kinerja
Jika kita menghadapi situasi ini, bagaimana kita bisa menonaktifkannya?
Menonaktifkan Cache Kueri
Ada dua opsi yang dapat Anda ubah. query_cache_type dan query_cache_size.
Jadi jika kita mengubah query_cache_size menjadi “0”, apakah itu berarti cache dinonaktifkan? query_cache_type? Or both? And does MySQL require a restart to avoid the global mutex?
Kode sumber menunjukkan ini kepada kita
kirim_hasil_ke_klien
Kerang1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int Cache_kueri. . send_result_to_client(THD * thd, const LEX_CSTRING &sql)
{
ulonglong engine_data;
Kueri_cache_kueri * kueri;
#ifndef EMBEDDED_LIBRARY
Blokir_cache_kueri * blok_result_pertama;
#berakhir jika
Query_cache_block * result_block;
Query_cache_block_table * block_table, *block_table_end;
char * cache_key = NULL;
size_t tot_length;
Query_cache_query_flags tanda;
DBUG_ENTER("Query_cache. send_result_to_client");
/*
Pengujian 'query_cache_size' tanpa a lock here is safe: the benda
kami dapat lepas adalah that the query won't be served from cache, but we
simpan di mutex mengunci in the case when query cache is disabled.
Lihat juga a catatan on double-check locking usage above.
*/
jika (dinonaktifkan() |. thd - >locked_tables_mode . .
thd - >variabel.query_cache_type == 0 . . query_cache_size == 0)
goto err;
. . .
MySQL akan memeriksa apakah cache kueri diaktifkan sebelum menguncinya. Itu memeriksa empat kondisi, dan salah satunya harus benar. Tiga yang terakhir mungkin sudah jelas, tapi apa fungsi “is_disabled()”? .
1
2
3
batal disable_query_cache(batal) { m_query_cache_is_disabled= TRUE; }
. . .
bool dinonaktifkan(batal) { return m_query_cache_is_disabled; }
Kueri_cache
C1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
batal Cache_kueri. . init()
{
DBUG_ENTER("Query_cache. init");
mysql_mutex_init(key_structure_guard_mutex,
&structure_guard_mutex, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_cache_status_changed,
&COND_cache_status_changed);
m_cache_lock_status = Cache_kueri. . BUKA KUNCI;
diinisialisasi = 1;
/*
Jika kita secara eksplisit mematikan query cache dari command line, query cache akan
dinonaktifkan untuk pengingat waktu hidup server. Ini karena kita
ingin menghindari mengunci mutex khusus QC jika cache kueri tidak mau
digunakan
*/
jika (variabel_sistem_global. query_cache_type == 0)
query_cache. disable_query_cache();
DBUG_VOID_RETURN;
}
Jika variabel_sistem_global. query_cache_type == 0 kondisi benar akan memanggil disable_query_cache . Artinya, jika kita menyetel function, which sets m_query_cache_is_disabled = True, so is_disabled going to be “True”. That means if we are setting query_cache_type ke 0 pada waktu proses, itu akan menghilangkan mutex global. Mari jalankan beberapa pengujian untuk mengonfirmasi hal ini dan lihat apakah global mutex menghilang setelah mengubah query_cache_type menjadi 0.
Menjalankan tes
Konteks pada tes
- Kami menjalankan tes OLTP sederhana menggunakan sysbench sebagai berikut
1
sysbench -- tes = "/usr/ . lua" -- laporan - interval=1 --max-time=120 --oltp-read-only=off --max-requests=0 --num-threads=4 --oltp-table-size=2000000 --mysql-host=localhost --mysql-db=test --db-driver=mysql --mysql-user=root run
- Bagian penting dari saya. file cnf
1
2
3
4
query_cache_type = 1
query_cache_limit = 1jt
query_cache_size = 1G
performance_schema_instrument = 'wait/synch/%Query_cache%=COUNTED'
Jadi pada dasarnya pengujian dijalankan selama dua menit masing-masing saat bermain dengan query_cache_type dan query_cache_size.
- Memulai MySQL dengan query_cache_type = 1 dan query_cache_size=1G.
- Ubah query_cache_type menjadi 0. Seperti yang kita lihat tidak ada yang berubah, MySQL masih menggunakan cache kueri.
- Tetapi ketika kami menghentikan sysbench dan memulai lagi (menutup dan membuka koneksi baru), kami dapat melihat tidak ada lagi penyisipan yang masuk ke cache kueri. Namun kami masih dapat melihat kueri seperti “Not Cache” yang berarti mengubah query_cache_type berlaku hanya untuk koneksi baru, dan kami masih dapat melihat beberapa mutex.
- Memulai ulang MySQL dengan query_cache_type = 0 dan query_cache_size=0. Finally, we disabled the query cache and all the mutex is disappeared.
- Mulai ulang MySQL dengan cache kueri diaktifkan
- Kami mengubah query_cache_size = 0 dan hampir berhasil .
- Mengubah query_cache_type = 0 dan memulai ulang sysbench tidak .
Jadi, satu-satunya cara untuk menghentikan aktivitas apa pun di sekitar cache kueri memerlukan memulai ulang MySQL dengan query_cache_type = 0 . Menonaktifkannya atau bahkan menyetelnya ke "0" saat runtime tidak sepenuhnya menghentikan aktivitas mutex. and query_cache_size=0. Disabling it or even set it to “0” on runtime is not completely stopping mutex activity.
Tetapi mengapa kita masih membutuhkan query_cache_size sementara dalam teori query_cache_type should be enough?
Seperti yang dirujuk di atas, manual mengatakan jika query_cache_type = 0
Jangan menyimpan hasil dalam cache atau mengambil hasil dari cache kueri. Perhatikan bahwa ini tidak membatalkan alokasi buffer cache kueri. Untuk melakukannya, Anda harus menyetel query_cache_size ke 0
Berdasarkan pengujian kami, jika kami mengubah query_cache_type menjadi 0, itu masih mengenai cache.
Jadi Anda mungkin berpikir “baiklah, saya tidak mengaktifkan cache kueri dan menggunakan default untuk tetap menonaktifkannya. ” Teruslah membaca, karena Anda mungkin salah. Menurut , mulai dari 5. 6. 8 query_cache_type = 0 disetel secara default, tetapi query_cache_size= 1048576 (1MB). This means that if we keep default configuration, we will still see activity in the query cache as follows:
Kerang1
2
3
4
5
6
7
8
9
10
11
12
13
mysql - e "tampilkan status global seperti 'qca%';"
+---------------------------------------+---------+
. Nama_Variabel . Nilai .
+---------------------------------------+---------+
. Qcache_free_blocks . 1 .
. Qcache_free_memory . 1031320 .
. Qcache_hits . 0 .
. Qcache_inserts . 0 .
. Qcache_lowmem_prunes . 0 .
. Qcache_not_cached . 423294 .
. Qcache_queries_in_cache . 0 .
. Qcache_total_blocks . 1 .
+---------------------------------------+---------+
Tetapi jika kita hanya menambahkan query_cache_size = 0 ke . cnf dan periksa lagi (tentu saja setelah me-restart server).
Kerang1
2
3
4
5
6
7
8
9
10
11
12
13
mysql - e "tampilkan status global seperti 'qca%';"
+---------------------------------------+-------+
. Nama_Variabel . Nilai .
+---------------------------------------+-------+
. Qcache_free_blocks . 0 .
. Qcache_free_memory . 0 .
. Qcache_hits . 0 .
. Qcache_inserts . 0 .
. Qcache_lowmem_prunes . 0 .
. Qcache_not_cached . 0 .
. Qcache_queries_in_cache . 0 .
. Qcache_total_blocks . 0 .
+---------------------------------------+-------+
Kami akhirnya tidak mendapatkan aktivitas terkait cache kueri sama sekali. Berapa banyak overhead yang disebabkan oleh ini?
Sekarang kami bertanya-tanya apakah kasus ini memerlukan laporan bug. Pantau terus, kami akan segera menerbitkan hasilnya di pos
Menggali lebih banyak kode
Mari kita lihat fungsi. MySQL menggunakan fungsi ini untuk menyimpan kueri dalam cache kueri. Jika kita membaca kode kita dapat menemukan ini
C1
2
jika (thd - >locked_tables_mode |. query_cache_size == 0)
DBUG_VOID_RETURN;
Ini hanya memeriksa query_cache_size , tidak memeriksa jenis. Store Query is called in , which also does not check the query_cache_type .
Kesimpulan
Ada kontradiksi antara memeriksa cache kueri dan menyimpan data di cache kueri, yang memerlukan penyelidikan lebih lanjut. Namun seperti yang dapat kita lihat, tidak mungkin untuk sepenuhnya menonaktifkan cache kueri dengan cepat dengan mengubah query_cache_type atau/dan query_cache_size to 0. Based on the code and the tests, if you want to make sure the query cache is fully disabled, change query_cache_size dan query_cache_type to 0 and restart MySQL.
Adalah fakta yang diketahui bahwa cache kueri dapat menjadi titik perdebatan yang besar, dan kami tidak mencoba untuk membandingkan overhead kinerja karena ini sebagian besar bergantung pada jenis beban kerja. Namun, kami masih dapat melihat beberapa overhead jika cache kueri tidak sepenuhnya dinonaktifkan saat MySQL dimulai