2010-11-12 3 views
0

Я получаю следующую ошибку довольно часто при запуске определенного запроса в моей базе данных (все таблицы используют механизм хранения InnoDB): «Тупик обнаружен при попытке получить блокировку, попробуйте перезапустить транзакцию»Deadlock, найденный в MySQL (InnoDB)

Этот запрос DELETE FROM sessions WHERE userid != 0 AND lastactivity < 1289594761 AND admin = 1 AND userid NOT IN (SELECT userid FROM users WHERE (userflags & 1048576))

ошибки начали происходить, когда я добавил не частично к моему WHERE заявление. Почему это вызывает проблемы и что я могу сделать, чтобы предотвратить это?

+0

Хотя вы можете свести к минимуму тупики, см. Http://stackoverflow.com/questions/2596005/working-around-mysql-error-deadlock-found-when-trying-to -get-lock-try-restartin – nos

ответ

2

Простым решением было бы разделить это на два последовательных запроса. I.e.,:

SELECT userid в #tmptable FROM users WHERE (userflags & 1048576);

УДАЛИТЬ ОТ сеансов WHERE userid! = 0 И lastactivity < 1289594761 И admin = 1 И userid NOT IN (выберите userid из #tmptable);

Таким образом, вы работаете с локальной копией сеанса значений из второй таблицы и не вызываете блокировку чтения на ней. Однако это просто быстрое и грязное решение. Лучшим решением было бы проанализировать параметры блокировки транзакций из всех действий, которые касаются этих двух таблиц, и переписать запрос, если вы будете повторно использовать его регулярно.

+0

Он просто используется в cronjob для обслуживания, поэтому, если он время от времени терпит неудачу, это не очень важно - я проигнорировал их уже несколько месяцев, но для удаления сообщений об ошибках уведомления становятся раздражающими. ;) – ThiefMaster

+0

Btw, я сделал это схожую с тем, что вы предложили, - не используя временную таблицу, а просто сохраняя список userid в моем PHP-коде, а затем используя 'IN (list, of, userids)' в инструкции DELETE , – ThiefMaster

2

Предположительно, вы чаще получаете ошибку, потому что теперь это гораздо более медленный запрос.

& ОП на userflags делает подзапрос незаменимым. Слова флага обычно не являются хорошим дизайном схемы, поскольку они требуют вычисления, которое поражает индексацию. Если вы часто выполняете запросы на бит-тестирование, отдельные столбцы с небольшими типами данных (например, TINYINT) могут быть лучше.

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

DELETE sessions 
FROM sessions 
JOIN users ON users.userid=sessions.userid 
WHERE sessions.lastactivity<1289594761 AND admin=1 
AND (users.userflags&1048576)=0 

(вливается на DELETE не является -ANSI SQL в MySQL.)

+0

Я думаю, вы хотели сказать 'AND NOT (users.userflags & 1048579)' - но хорошее предложение, я не думал об использовании JOIN, потому что IMO, использующая JOINs в инструкции DELETE/UPDATE, делает запрос более нечетным – ThiefMaster

+0

А, да , вы правы. – bobince