Como matar automaticamente consultas lentas do MySQL após N segundos?

16

Estou procurando um script bash bem testado (ou solução alternativa) para fazer isso, a fim de evitar que max_connection se esgote. Eu sei que ele está combatendo os sintomas, mas realmente precisa de um script como uma solução de curto prazo.

alfish
fonte
Qual versão do MySQL você está executando ???
RolandoMySQLDBA
a versão do mysql é 5.5
alfish

Respostas:

21

confira o comando pt-kill do kit de ferramentas percona .

e .. comece a monitorar seu sistema - munin , cactos com melhores modelos de cactos para mysql , qualquer coisa para que você tenha uma idéia do que está acontecendo. registrar consultas lentas do mysql também será uma boa idéia.

pQd
fonte
Obrigado pelas sugestões, mas procure realmente um script "único".
alfish
5
pt-kill é muito bem testado e resolve seu problema exato. Demora 10 minutos para descobrir os parâmetros da linha de comando e colocá-lo em execução. O que mais você quer?
Aaron Brown
12

Se você possui o MySQL 5.1 onde a lista de processos está no arquivo Information_SCHEMA, você pode fazer isso para gerar os comandos KILL QUERY em massa a partir do cliente mysql para consultas em execução por mais de 20 minutos (1200 segundos):

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

Você pode fazer cláusulas WHERE no campo INFO para procurar uma consulta específica, o campo TIME em consultas demoradas ou o campo DB em um banco de dados específico.

Se você é root @ localhost, deve ter privilégios totais para executar isso da seguinte maneira

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

Você pode criar isso da seguinte maneira:

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

Aqui está outra variação:

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 Você não especificou um myDB desde que eu expliquei a leitura de information_schema.processlist como um nome de tabela totalmente qualificado.

Aqui está uma demonstração do que você deve ver. Neste exemplo, ecoarei o comando KILL de todos os processos cujo tempo> 20000 segundos:

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

Eu tenho feito essa técnica nos últimos 5 anos. De fato, enviei essa resposta ao DBA StackExchange no ano passado e ela foi aceita .

RolandoMySQLDBA
fonte
Roland, você poderia esclarecer estas questões: Este comando é persistente ou precisa ser executado com freqüência? O que deve ser substituído no comando assumindo que o usuário do mysql seja 'root' e o nome do banco de dados seja myDB? Graças
alfish
Eu atualizei minha resposta.
RolandoMySQLDBA
Obrigado, mas depois de transformar sua última receita em um script bash, recebo: ERRO 1064 (42000) na linha 1: Você tem um erro na sintaxe do SQL; verifique o manual que corresponde ao seu servidor MySQL versão para o direito de utilizar sintaxe próximo a '' at line 1
alfish
Qual comando SQL gerou esse erro ??
RolandoMySQLDBA
Roland SF não é uma caixa de areia. Melhor testar antes de sugerir algo como solução. Além disso, quando todas as conexões estão saturadas, como o mysql executará seu procedimento, assumindo que não está com defeito?
alfish
6

Encontrei o seguinte código cortado aqui :

Atualização 2013-01-14: Houve uma dica anônima de que isso é potencialmente perigoso e pode também matar processos de replicação. Portanto, use por sua conta e risco:

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
fonte
Qual é a constante de tempo no snippet acima?
alfish
@alfish Não sou o autor - mas eu diria que essa é uma expressão regular que corresponde a todos os valores de tempo que possuem pelo menos dois dígitos. Portanto, a suposição aqui é que 10 é muito longo.
Nils
1

No MySQL 5.7 em diante, você pode usar a variável max_execution_time para fazer isso automaticamente em todas as consultas de leitura "SELECT".

Pawan Gaur
fonte
0

Eu não tentaria soluções bash se você gostaria de tempo de atividade!

Se você tiver acesso ao código, poderá realmente definir o tempo máximo de execução nas instruções SELECT usando o método descrito aqui :

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

Caso contrário, no servidor:

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

Instale pt-kill:

$ wget percona.com/get/pt-kill

Tire uma foto da sua lista de processos:

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

Teste pt-kill no instantâneo:

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

Verifique se as regras de correspondência se adequam ao seu caso. Os itens acima eliminarão todas as instruções Execute por 45 segundos. Depois de ter certeza, modifique e execute este comando para executar a instrução em um intervalo de 10 segundos:

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