Jak automatycznie zabijać wolne zapytania MySQL po N sekundach?

16

Szukam dobrze przetestowanego skryptu bash (lub alternatywnego rozwiązania), aby to zrobić, aby uniknąć wyczerpania max_connection. Wiem, że walczy z objawami, ale naprawdę potrzebuję takiego skryptu jako rozwiązania krótkoterminowego.

alfish
źródło
Jaką wersję MySQL używasz ???
RolandoMySQLDBA
wersja mysql to 5.5
alfish

Odpowiedzi:

21

sprawdź komendę pt-kill z zestawu narzędzi Percona .

i ... zacznij monitorować swój system - munin , kaktusy z lepszymi szablonami kaktusów dla mysql , wszystko, żebyś miał pojęcie, co się dzieje. dobrym pomysłem będzie także rejestrowanie powolnych zapytań mysql.

pQd
źródło
Dzięki za sugestie, ale naprawdę szukaj skryptu „jednorazowego”.
alfish
5
pt-kill jest bardzo dobrze przetestowany i rozwiązuje twój dokładny problem. Wyszukanie parametrów wiersza poleceń i uruchomienie go zajmuje około 10 minut. Czego jeszcze chcesz?
Aaron Brown,
12

Jeśli masz MySQL 5.1, w którym lista procesów znajduje się w pliku INFORMACJE_SCHEMA, możesz to zrobić, aby wygenerować polecenia KILL QUERY zbiorczo z poziomu klienta mysql dla zapytania trwającego dłużej niż 20 minut (1200 sekund):

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

Możesz wykonać klauzule WHERE w polu INFO, aby wyszukać określone zapytanie, pole TIME w przypadku zapytań długo działających lub pole DB w przypadku określonej bazy danych.

Jeśli jesteś rootem @ localhost, powinieneś mieć pełne uprawnienia do uruchamiania tego w następujący sposób

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

Możesz to zrobić w następujący sposób:

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

Oto kolejna odmiana:

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 Nie określiłeś myDB, ponieważ wyraźnie odczytałem z information_schema.processlist jako w pełni kwalifikowaną tablename.

Oto pokaz tego, co powinieneś zobaczyć. W tym przykładzie powtórzę polecenie KILL wszystkich procesów, których czas> 20000 sekund:

[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@***** ~]#

Robię tę technikę przez ostatnie 5 lat. W rzeczywistości przesłałem tę odpowiedź do DBA StackExchange w zeszłym roku i została zaakceptowana .

RolandoMySQLDBA
źródło
Roland, czy możesz wyjaśnić: Czy to polecenie jest trwałe, czy też trzeba je często uruchamiać? Co należy zastąpić w poleceniu, zakładając, że użytkownik mysql to „root”, a nazwa bazy danych to myDB? Dzięki
alfish,
Zaktualizowałem swoją odpowiedź.
RolandoMySQLDBA
Dzięki, ale po przekształceniu ostatniego przepisu w skrypt bash otrzymuję: BŁĄD 1064 (42000) w wierszu 1: Wystąpił błąd w składni SQL; sprawdź instrukcję, która odpowiada twojej wersji serwera MySQL pod kątem właściwej składni do użycia w pobliżu '' w linii 1
alfish
Które polecenie SQL wygenerowało ten błąd?
RolandoMySQLDBA
Roland SF nie jest piaskownicą. Lepiej przetestować, zanim zaproponujesz coś jako rozwiązanie. Ponadto, kiedy wszystkie połączenia są nasycone, w jaki sposób mysql uruchomi twoją procedurę, zakładając, że nie jest wadliwa?
Alfish
6

Znalazłem następujący kod-snipped tutaj :

Aktualizacja 14.01.2013: Anonimowo zasugerowano, że jest to potencjalnie niebezpieczne i może również zabić procesy replikacji. Korzystaj więc na własne ryzyko:

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
źródło
Jaka jest stała czasowa w powyższym fragmencie?
Alfish
@alfish Nie jestem autorem - ale powiedziałbym, że to wyrażenie regularne, które pasuje do wszystkich wartości czasowych, które mają co najmniej dwie cyfry. Zakłada się więc, że 10 jest za długie.
Nils
1

MySQL 5.7 i nowsze, możesz użyć zmiennej max_execution_time, aby zrobić to automatycznie dla wszystkich zapytań odczytu „SELECT”.

Pawan Gaur
źródło
0

Nie spróbowałbym rozwiązań bash, jeśli lubisz czas pracy!

Jeśli masz dostęp do kodu, możesz faktycznie ustawić maksymalny czas wykonywania instrukcji SELECT przy użyciu metody opisanej tutaj :

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

W przeciwnym razie na serwerze:

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

Zainstaluj pt-kill:

$ wget percona.com/get/pt-kill

Zrób migawkę swojej listy procesów:

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

Test pt-kill na migawce:

$ ./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

Upewnij się, że zasady dopasowania odpowiadają twojej sprawie. Powyższe zabije wszystkie instrukcje wykonania w ciągu 45 sekund. Gdy masz pewność, zmodyfikuj i uruchom tę komendę, aby wykonać instrukcję w odstępie 10 sekund:

$ ./pt-kill -u root -p myreallyimportantpassword --busy-time 45 --kill-busy-commands 'Execute' --victims all --interval 10 --kill
Geoff_Clapp
źródło