Lock wait timeout exceeded в Mysql

Thank you for reading this post, don't forget to subscribe!

Ошиб­ка "Lock wait timeout exceeded" воз­ни­ка­ет в MySQL'e, когда при­ло­же­ние пыта­ет­ся обно­вить запись, кото­рая забло­ки­ро­ва­на. Такое может про­изой­ти в двух случаях:

1. Обновление большого количества записей

При­ло­же­ние обнов­ля­ет сра­зу мно­го запи­сей в таблице:

# такой запрос обно­вит все запи­си в таблице

Пока запрос будет выпол­нять­ся, будут забло­ки­ро­ва­ны все запи­си. При­чем каж­дая запись будет забло­ки­ро­ва­на, пока не про­изой­дет пол­ное обнов­ле­ние таблицы.

2. Частое обновление одной записи

Слиш­ком частое обнов­ле­ние одной запи­си может при­ве­сти к той же проблеме:

# такая опе­ра­ция может выпол­нять­ся несколь­ко тысяч раз в секунду

Если про­бле­ма про­ис­хо­дит пря­мо сей­час, необ­хо­ди­мо узнать, какие про­цес­сы при­во­дят к ката­стро­фе. Обра­тить вни­ма­ние нуж­но на про­цес­сы, вре­мя испол­не­ния кото­рых боль­ше несколь­ких секунд:

# такой стрем­ный запрос луч­ше вооб­ще нико­гда не выполнять

Такой про­цесс необ­хо­ди­мо уничтожить:

Если до выкатки еще неделя

Если про­бле­ма наблю­да­ет­ся часто, но нет воз­мож­но­сти опе­ра­тив­но вне­сти изме­не­ния в при­ло­же­ние, сле­ду­ет уве­ли­чить вре­мя ожи­да­ния в настройках:

# зна­че­ние в секун­дах, по умол­ча­нию 50 секунд

Или в my.cnf (но тогда нуж­но пере­за­пус­кать MySQL):

Правильное решение

Нуж­но пони­мать, что при­чи­на этой ошиб­ки — это про­бле­ма при­ло­же­ния, а не MySQL. Что­бы это почи­нить, необ­хо­ди­мо вне­сти про­стые изме­не­ния в приложение.

Стра­те­гия — умень­шить коли­че­ство обнов­ле­ний одних и тех же запи­сей. Это дости­га­ет­ся с помо­щью про­ме­жу­точ­ной таб­ли­цы и асин­хрон­ной зада­чи по обнов­ле­нию данных: #

Пред­ста­вим, что мы будем решать про­бле­му для таб­ли­цы, в кото­рой часто обнов­ля­ют­ся несколь­ко запи­сей. Такая структура:

Типич­ный запрос, кото­рый вызы­ва­ет проблемы:

# запро­сов очень мно­го, полу­ча­ем "Lock wait timeout exceeded"

Необ­хо­ди­мо создать про­ме­жу­точ­ную таблицу:

Вме­сто обнов­ле­ний глав­ной таб­ли­цы, все дан­ные с обнов­ле­ни­я­ми необ­хо­ди­мо встав­лять в таб­ли­цу log:

# Т.е. вме­сто обнов­ле­ния, посто­ян­но допи­сы­ва­ем новые запи­си в таб­ли­цу "log"

Завер­ша­ю­щий этап — это асин­хрон­ная зада­ча, кото­рая будет агре­ги­ро­вать и обнов­лять глав­ную таб­ли­цу раз в 10 секунд. Запус­кать агре­га­цию мож­но кро­ном, либо исполь­зо­вать supervisor для запус­ка про­цес­са в фон.

В каче­стве таб­ли­цы лога хоро­шо подой­дет дви­жок MEMORY. Если дан­ные очень кри­тич­ны к поте­рям — толь­ко InnoDB.

Ошиб­ка "Lock wait timeout exceeded" встре­ча­ет­ся при частом обнов­ле­нии одних и тех же дан­ных. Для быст­ро­го реше­ния про­бле­мы доста­точ­но изме­нить настрой­ку innodb_lock_wait_timeout. Асин­хрон­ная агре­га­ция и обнов­ле­ние поз­во­лят изба­вить­ся от этой проблемы.