Bagaimana cara secara otomatis membunuh permintaan MySQL lambat setelah N detik?

16

Saya mencari skrip bash yang teruji dengan baik (atau solusi alternatif) untuk melakukannya, untuk menghindari max_connection menjadi habis. Saya tahu itu melawan gejala, tetapi benar-benar membutuhkan skrip seperti solusi jangka pendek.

alfish
sumber
Apa versi MySQL yang Anda jalankan ???
RolandoMySQLDBA
versi mysql adalah 5.5
alfish

Jawaban:

21

lihat perintah pt-kill dari perkona toolkit .

dan .. mulailah memonitor sistem Anda - munin , kaktus dengan templat kaktus yang lebih baik untuk mysql , apa pun sehingga Anda mendapatkan ide tentang apa yang terjadi. mencatat permintaan lambat mysql akan menjadi ide yang bagus juga.

pQd
sumber
Terima kasih tentang sarannya, tetapi benar-benar mencari skrip "satu kali".
alfish
5
pt-kill telah teruji dengan sangat baik dan menyelesaikan masalah Anda yang sebenarnya. Diperlukan waktu 10 menit untuk mengetahui parameter baris perintah dan menjalankannya. Apa lagi yang kamu inginkan?
Aaron Brown
12

Jika Anda memiliki MySQL 5.1 di mana daftar proses ada di INFORMATION_SCHEMA, Anda dapat melakukan ini untuk menghasilkan perintah KILL QUERY dalam jumlah besar dari dalam klien mysql untuk kueri yang berjalan lebih dari 20 menit (1200 detik):

SELECT GROUP_CONCAT(CONCAT('KILL QUERY ',id,';') SEPARATOR ' ') KillQuery
FROM information_schema.processlist WHERE user<>'system user'
AND time >= 1200\G

Anda bisa melakukan klausa WHERE terhadap bidang INFO untuk mencari kueri tertentu, bidang TIME terhadap kueri yang sudah berjalan lama, atau bidang DB terhadap database tertentu.

Jika Anda root @ localhost, Anda harus memiliki hak penuh untuk menjalankan ini sebagai berikut

SECONDS_TOO_LONG=1200
KILLPROC_SQLSTMT="SELECT GROUP_CONCAT(CONCAT('KILL QUERY ',id,';') SEPARATOR ' ') KillQuery FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"
mysql -uroot -ppassword -ANe"${KILLPROC_SQLSTMT}" | mysql -uroot -ppassword

Anda dapat crontab ini sebagai berikut:

SECONDS_TOO_LONG=1200
QUERIES_RUNNING_TOO_LONG=`mysql -uroot -ppassword -ANe"SELECT COUNT(1) FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"`
if [ ${QUERIES_RUNNING_TOO_LONG} -gt 0 ]
then
    KILLPROC_SQLSTMT="SELECT GROUP_CONCAT(CONCAT('KILL QUERY ',id,';') SEPARATOR ' ') KillQuery FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"
    mysql -uroot -ppassword -ANe"${KILLPROC_SQLSTMT}" | mysql -uroot -ppassword
fi

Berikut variasi lainnya:

SECONDS_TOO_LONG=1200
QUERIES_RUNNING_TOO_LONG=`mysql -uroot -ppassword -ANe"SELECT COUNT(1) FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"`
if [ ${QUERIES_RUNNING_TOO_LONG} -gt 0 ]
then
    KILLPROC_SQLSTMT="SELECT CONCAT('KILL QUERY ',id,';') KillQuery FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"
    mysql -uroot -ppassword -ANe"${KILLPROC_SQLSTMT}" > /tmp/kill_log_queries.sql
    mysql -uroot -ppassword < /tmp/kill_log_queries.sql
fi

BTW Anda tidak harus menentukan myDB karena saya secara eksplisit membaca dari information_schema.processlist sebagai nama tab yang sepenuhnya memenuhi syarat.

Ini adalah demonstrasi dari apa yang harus Anda lihat. Untuk contoh ini, saya akan mengulangi perintah KILL dari semua proses yang waktunya> 20000 detik:

[root@***** ~]# mysql `lwdba_connect` -ANe"SELECT GROUP_CONCAT('KILL ',id,'; ' SEPARATOR ' ') FROM information_schema.processlist WHERE time > 25000 AND user<>'system user';"
+----------------------------------------------------+
| KILL 180186;  KILL 180141;  KILL 176419;  KILL 3;  |
+----------------------------------------------------+
[root@***** ~]#

Saya telah melakukan teknik ini selama 5 tahun terakhir. Bahkan, saya mengirimkan jawaban ini ke DBA StackExchange tahun lalu dan diterima .

RolandoMySQLDBA
sumber
Roland, bisakah Anda menjelaskan ini: Apakah perintah ini persisten, atau perlu sering dijalankan? Apa yang harus diganti dalam perintah dengan asumsi bahwa pengguna mysql adalah 'root' dan nama database adalah myDB? Terima kasih
alfish
Saya memperbarui jawaban saya.
RolandoMySQLDBA
Terima kasih, tetapi setelah mengubah resep terakhir Anda menjadi skrip bash, saya mendapatkan: ERROR 1064 (42000) di baris 1: Anda memiliki kesalahan dalam sintaks SQL Anda; periksa manual yang sesuai dengan versi server MySQL Anda untuk sintaks yang tepat untuk digunakan di dekat '' pada baris 1
alfish
Perintah SQL mana yang menghasilkan kesalahan itu ???
RolandoMySQLDBA
Roland SF bukan kotak pasir. Lebih baik untuk menguji sebelum Anda menyarankan sesuatu sebagai solusi. Selain itu, ketika semua koneksi sudah jenuh, bagaimana mysql akan menjalankan prosedur Anda, dengan asumsi bahwa itu tidak salah?
alfish
6

Saya menemukan potongan kode berikut di sini :

Pembaruan 2013-01-14: Ada petunjuk anonim bahwa ini berpotensi berbahaya dan dapat membunuh proses replikasi juga. Jadi gunakan dengan risiko Anda sendiri:

mysql -e 'show processlist\G' |\
egrep -b5 'Time: [0-9]{2,}' |\
grep 'Id:' |\
cut -d':' -f2 |\
sed 's/^ //' |\
while read id
do
  mysql -e "kill $id;"
done
Nils
sumber
Berapa konstanta waktu dalam cuplikan di atas?
alfish
@alfish Saya bukan penulis - tetapi saya akan mengatakan ini adalah ekspresi reguler yang cocok dengan semua nilai waktu yang memiliki setidaknya dua digit. Jadi asumsi di sini adalah 10 terlalu panjang.
Nils
1

MySQL 5.7 dan seterusnya Anda dapat menggunakan variabel max_execution_time untuk menyelesaikan ini secara otomatis untuk semua permintaan baca "SELECT".

Pawan Gaur
sumber
0

Saya tidak akan mencoba solusi bash jika Anda suka waktu aktif!

Jika Anda memiliki akses ke kode Anda sebenarnya dapat mengatur waktu eksekusi maksimum pada pernyataan SELECT menggunakan metode yang diuraikan di sini :

SELECT 
MAX_EXECUTION_TIME = 1000 --in milliseconds
* 
FROM table;

Jika tidak, di server:

/programming/415905/how-to-set-a-maximum-execution-time-for-a-mysql-query

Pasang pt-kill:

$ wget percona.com/get/pt-kill

Ambil snapshot dari daftar proses Anda:

$ mysql -u root -B -pmyreallyimportantpassword -e "show processlist;" > processlist.txt

Uji pt-kill pada snapshot:

$ ./pt-kill --test-matching processlist.txt --busy-time 45 --kill-busy-commands 'Execute' --victims all --print
# 2019-02-25T17:34:37 KILL 45710302 (Execute 374 sec) SELECT\n\tCOUNT(DISTINCT(LP.sessionId))\nFROM lp_traffic LP\nINNER JOIN orders O ON O.orderId = LP.order
# 2019-02-25T17:34:37 KILL 45713515 (Execute 67 sec) SELECT \n\tCOUNT(DISTINCT(CASE WHEN T.response = 'SUCCESS' AND T.isVoid = 0 AND (T.txnType IN

Pastikan aturan kecocokan cocok dengan kasus Anda. Ini di atas akan membunuh semua pernyataan Jalankan lebih dari 45 detik. Setelah Anda yakin maka modifikasi dan jalankan perintah ini untuk menjalankan pernyataan pada interval 10 detik:

$ ./pt-kill -u root -p myreallyimportantpassword --busy-time 45 --kill-busy-commands 'Execute' --victims all --interval 10 --kill
Geoff_Clapp
sumber