Многим новичкам интересно знать, откуда берутся эти спящие запросы в мускуле, а все очень просто.
Давайте освежим память перечнем того, как все работает.
Вроде бы все отлично, и никаких проблем быть не может, но они появляются на шаге 6, потому, что до шага 7 дело не доходит.
А может это быть потому, что PHP скрипт не завершает свою работу, и обычно причина:
Собственно все, исправляйте одну из этих ошибок и SLEEP-запросов не будет.
Подробнее
А теперь подробнее, для тех кто не поверил и хочет разобраться подробнее:
mysql> show full processlist;
+-----+------------+-----------+------------+---------+------+-------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------------+-----------+------------+---------+------+-------+-----------------------+
| 127 | putin | localhost | mydb | Sleep | 895 | | NULL |
| 130 | root | localhost | mydb | Query | 0 | NULL | show full processlist |
+-----+------------+-----------+------------+---------+------+-------+-----------------------+итак, мы видим зависший (уснувший) MySQL-процесс, и в довесок к нему мы можем увидеть зависший Apache-процесс
# netstat -ntp
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp 0 0 127.0.0.1:80 127.0.0.1:47733 ESTABLISHED 27420/apache2
...а давайте убьем Apache-процесс, ведь это должно отразиться и на MySQL:
# service apache2 restart
готово, теперь смотрим на MySQL-процессы:
mysql> show full processlist;
+-----+------------+-----------+------------+---------+------+-------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------------+-----------+------------+---------+------+-------+-----------------------+
| 130 | root | localhost | mydb | Query | 0 | NULL | show full processlist |
+-----+------------+-----------+------------+---------+------+-------+-----------------------+и видим, что спящего процесса нет, а это значит, что убив Apache-процесс, у нас убился PHP-процесс, который закрыл соединение с MySQL.
Это процесс, который отработал и который теперь ждет, что клиент с помощью указателя выполнит считывание результатов запроса.
SLEEP процесс никак не влиеют на производительность. Однако, он использует несколько байт, чтобы поддержать текущее соединение с клиентом.
Вернемся к ситуации выше (клиент может подключиться и упасть не закрыв соединение или клиент ушел в рекурсию и поэтому держит подключение), тогда SLEEP процесс будет висеть до истечения таймаута. Вот тут может возникнуть другая проблема - у Вас наступит сутуация с лимитом подключений к MySQL. Чтобы такой ситуации не произошло, Вам нужно чтобы соединения закрывались по тайм-ауту, который можно указать следующими параметрами:
Указать параметры можно следующими способами:
1. Указать параметры в файле my.cnf (не забудьте затем перезапустить mysql):
[mysqld]
interactive_timeout=180
wait_timeout=180
2. Если Вы не хотите перезапускать mysql, тогда в mysql-клиенте выполните две команды:
SET GLOBAL interactive_timeout = 180;
SET GLOBAL wait_timeout = 180;
3. Сделать, чтобы клиент (например PHP) указал данные параметры для текущей сессии:
<?php
mysql_connect('127.0.0.1:3351', 'root', '');
mysql_select_db('test');
mysql_query('set wait_timeout=1');
$result = mysql_query( 'SELECT * FROM table');
p.s. Есть и другие интересные параметры:
mysql> show variables like '%timeout%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| connect_timeout | 10 | - кол-во секунд, прежде чем mysql ответит: Bad handshake
| interactive_timeout | 28800 |
| net_read_timeout | 30 |
| net_write_timeout | 60 |
| wait_timeout | 28800 |
+----------------------------+-------+Посмотреть все или значение оперделенной переменной можно так:
SHOW VARIABLES WHERE Variable_name='connect_timeout'или так:
SELECT @@GLOBAL.connect_timeout; -- 10 сек
SELECT @@connect_timeout; -- 10 секДавайте поиграемся с временем выполнения и прерыванием.
В MySQL можно задать максимальное время выполнения запросов, и сделать это можно для всех сессий или для текущей. Давайте посмотрим текущие значения (по-умолчанию значения равны нулям - значит ограничения нет):
SELECT @@GLOBAL.MAX_EXECUTION_TIME, @@SESSION.MAX_EXECUTION_TIME;Оба значения указывают время в миллисекундах (1000 мс = 1 секунда).
Давайте дле текущей сессии установим время выполнения 3 секунды:
SET SESSION MAX_EXECUTION_TIME=3000;Теперь проверим, будет ли наш запрос выполняться более 3 секунд, для этого воспользуемся функцией SLEEP:
SELECT SLEEP(4);Ура, запрос работал всего 3 секунды, но при завершении вернул значение 1 - это значит он был прерван. Если указать значение 2 секунды, то такой запрос возвратит 0 - что значит, что запрос выполнен полностью.
Кто-то скажет, как же так, почему запрос выполнился, а не прервался с ошибкой? Все дело в том, что функция SLEEP знает про значение MAX_EXECUTION_TIME и не будет работать, больше указанного времени. А вот реальный запрос работает иначе:
SELECT 'hi' FROM t1 WHERE SLEEP(1);ОЧЕНЬ ВАЖНО: такой запрос будет делать паузу 1 секунда при чтении каждой строки из таблицы t1 (это значит, сколько строк в таблице, столько секунд и будет выполняться запрос).
Например, если в таблице t1 имеется 4 строки, то такой запрос будет работать 4 секунды и завершится с ошибкой:
[HY000][3024] Query execution was interrupted, maximum statement execution time exceededведь выше мы задали максимальное время выполнения 3 секунды: MAX_EXECUTION_TIME=3000
p.s. функция benchmark так же как SLEEP и не выполняется больше MAX_EXECUTION_TIME
select benchmark(9999999999, md5('when will it end?'));Следующие две команды эквивалентны:
SHOW FULL PROCESSLIST;
SELECT * FROM information_schema.processlist;
Так же результат можно представить из списка процессов, любым удобным для Вас способом. Есть некоторые базовые примеры.
Показать количество подключений для каждого пользователя:
SELECT 'USER', COUNT(*) FROM information_schema.processlist GROUP BY 'USER';
Показать количество подключений по каждому хосту:
SELECT 'HOST', COUNT(*) FROM information_schema.processlist GROUP BY 'HOST';
Показать активности пользователя root:
SELECT * FROM information_schema.processlist WHERE 'USER' = 'root';
Показать процессы подобные указанному:
SELECT * FROM information_schema.processlist WHERE 'INFO' LIKE 'SELECT %';
Показать среднее время запроса для каждой базы данных:
SELECT 'DB', AVG('TIME') FROM information_schema.processlist GROUP BY 'DB';
Лог медленных запросов - способ, помогающий понять, какие SQL-запросы мы должны оптимизировать в приложении.
Данный механизм по-умолчанию выключен и настроен на запись в файл при выполнении запроса более 10 секунд:
SELECT @@GLOBAL.slow_query_log; -- 0 - выключен
SELECT @@GLOBAL.log_output; -- FILE
SELECT @@GLOBAL.long_query_time; -- 10
или так:
SELECT @@slow_query_log; -- 0 - выключен
SELECT @@log_output; -- FILE
SELECT @@long_query_time; -- 10Давайте включим + т.к. грепать лог-файл неудобно, пропросим MySQL писать в таблицу и писать только в том случае, если запрос выполняется дольше 1 секунды:
SET global slow_query_log = 1;
SET global log_output = 'table';
SET global long_query_time = 1;Теперь ждем, пока приложение сделает медленные запросы (или делаем их самостоятельно - см. выше как) и смотрим логи, например таким запросом:
SELECT * FROM mysql.slow_log WHERE query_time > 1 ORDER BY slow_log.start_time DESC; Из интересного:
UPDATES запросам нужна блокировка записи.Удачки в разработке!