2009-07-02 4 views
3

Специальный запрос UPDATE на нашем сайте иногда работает очень медленно и проверяет гораздо больше строк, чем необходимо. Он фильтрует первичный ключ, поэтому я бы ожидал, что MySQL всегда просто нужно будет изучить одну строку.MySQL UPDATE-запрос по первичному ключу иногда чрезвычайно медленный

Вот некоторые примеры из журнала медленных запросов в MySQL:

# Time: 090702 12:59:06 
# [email protected]: XXX[XXX] @ XXX [XXX] 
# Query_time: 21 Lock_time: 0 Rows_sent: 0 Rows_examined: 500500 
SET timestamp=1246532346; 
UPDATE `folders` SET `folder_id` = '1705641', `updated_at` = now() WHERE `folders`.`id` = '1682995'; 
# Time: 090702 14:13:44 
# [email protected]: XXX[XXX] @ XXX [XXX] 
# Query_time: 17 Lock_time: 0 Rows_sent: 0 Rows_examined: 16816745 
SET timestamp=1246536824; 
UPDATE `folders` SET `folder_id` = '417997', `updated_at` = now() WHERE `folders`.`id` = '1705956'; 
# Time: 090702 14:15:42 
# [email protected]: XXX[XXX] @ XXX [XXX] 
# Query_time: 13 Lock_time: 0 Rows_sent: 0 Rows_examined: 16816719 
SET timestamp=1246536942; 
UPDATE `folders` SET `folder_id` = '1706267', `updated_at` = now() WHERE `folders`.`id` = '1705956'; 
# Time: 090702 16:07:43 
# [email protected]: XXX[XXX] @ XXX [XXX] 
# Query_time: 499 Lock_time: 0 Rows_sent: 0 Rows_examined: 88668449 
SET timestamp=1246543663; 
UPDATE `folders` SET `folder_id` = '1707407', `updated_at` = now() WHERE `folders`.`id` = '1706992'; 

Запрос выполняется чаще, чем это, хотя, так что не всегда подвергать это поведение. Кроме того, если я запускаю одни и те же запросы вручную, они работают просто отлично и сразу возвращаются.

Я также проверил таблицу, и, насколько я могу видеть, что это должно быть хорошо:

mysql> describe folders; 
+------------------+-----------------------+------+-----+---------------------+----------------+ 
| Field   | Type     | Null | Key | Default    | Extra   | 
+------------------+-----------------------+------+-----+---------------------+----------------+ 
| id    | mediumint(8) unsigned | NO | PRI | NULL    | auto_increment | 
| user_id   | mediumint(8) unsigned | NO | MUL | NULL    |    | 
| folder_id  | mediumint(8) unsigned | YES | MUL | NULL    |    | 
| created_at  | timestamp    | NO |  | CURRENT_TIMESTAMP |    | 
| updated_at  | timestamp    | NO |  | 0000-00-00 00:00:00 |    | 
| modified_at  | timestamp    | NO |  | 0000-00-00 00:00:00 |    | 
| name    | varchar(255)   | NO |  | NULL    |    | 
| guest_permission | tinyint(3) unsigned | NO |  | 1     |    | 
+------------------+-----------------------+------+-----+---------------------+----------------+ 
8 rows in set (0.00 sec) 

mysql> show index from folders; 
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| folders |   0 | PRIMARY |   1 | id   | A   |  760318 |  NULL | NULL |  | BTREE  |   | 
| folders |   1 | user_id |   1 | user_id  | A   |  69119 |  NULL | NULL |  | BTREE  |   | 
| folders |   1 | folder_id |   1 | folder_id | A   |  380159 |  NULL | NULL | YES | BTREE  |   | 
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
3 rows in set (0.00 sec) 

Другое дело в том, что сервер MySQL иногда зависает и перестает принимать соединения, и каждый раз, когда это происходит, я обычно найдите один из этих медленных запросов в файле журнала непосредственно перед сбоем. Я не вижу соответствующих сообщений об ошибках в других файлах журналов, но перезапуск MySQL заставляет его реагировать снова.

У кого-нибудь есть идея, что происходит, или что я могу проверить, чтобы сузить проблему?

EDIT: Мы используем MySQL 5.0.51a на выделенном сервере и в настоящее время 6 веб-серверов, работающих под управлением PHP 5.2.6 и подключающихся через PDO к серверу MySQL. На всех серверах работает Debian Lenny. Медленный запрос выполняется на всех веб-серверах.

EDIT: Вот EXPLAIN для соответствующего запроса, и без кавычек:

mysql> explain SELECT * FROM `folders` WHERE `folders`.`id` = '1682995'; 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra | 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+ 
| 1 | SIMPLE  | folders | const | PRIMARY  | PRIMARY | 3  | const | 1 |  | 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+ 
1 row in set (0.00 sec) 

mysql> explain SELECT * FROM `folders` WHERE `folders`.`id` = 1682995; 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra | 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+ 
| 1 | SIMPLE  | folders | const | PRIMARY  | PRIMARY | 3  | const | 1 |  | 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+ 
1 row in set (0.00 sec) 
+0

Это действительно интересно, я хотел бы знать, почему он это делает. Вы пробовали EXPLAIN SELECT * FROM 'folders' WHERE' id' = что-то? –

ответ

1

Глупый меня. Я забыл, что на столе folders были некоторые триггеры, и, конечно, это был запрос внутри одного из этих триггеров, который вызвал мои проблемы ...

Есть дополнительная таблица tree, которая поддерживает ассоциации между папками и триггеры обновляют эти ассоциации, когда папка удаляется или перемещается в иерархии. В триггере UPDATE он должен удалить все существующие ссылки в папку, прежде чем добавлять новые ссылки. Соответствующий запрос DELETE начал следующим образом:

DELETE FROM `tree` 
    WHERE `folder_id` IN (
    SELECT `folder_id` FROM `children` 
) 
    AND ... 

children это временная таблица, в которой я ранее хранить папки идентификаторы мне нужно. Теперь почему MySQL не может оптимизировать этот запрос, но если я использую RIGHT JOIN вместо этого он отлично работает:

DELETE `tree`.* FROM `tree` 
    RIGHT JOIN children USING (folder_id) 
    WHERE ... 

Поскольку я изменил этот запрос журнала медленно запросов в MySQL остается блаженно пуст, и мы не испытывали никаких блокировок MySQL.

1

Это странно, но я думаю, это может быть потому, что ваше поле идентификатор INT, но вы передаете строка (цитируется). Не пытайтесь использовать строку и посмотрите, помогает ли она.

Чтобы точно определить, что происходит, измените запрос на SELECT с тем же предложением WHERE и запустите его через EXPLAIN. Например:

EXPLAIN SELECT * FROM `folders` WHERE `folders`.`id` = '1682995'; 
EXPLAIN SELECT * FROM `folders` WHERE `folders`.`id` = 1682995; 

Посмотрите, есть ли разница.

More info on EXPLAIN.

+0

Без разницы. Кроме того, если это действительно было причиной, я бы ожидал, что запрос ВСЕГДА будет таким медленным. Но спасибо за помощь, во всяком случае! – toupeira

Смежные вопросы