Когда все пытаются изменить одну и ту же строку (гильдейский склад, популярный лот на аукционе, общий счётчик сервера), блокировку получает только один запрос за раз.
Почему Из-за события или популярного предмета все изменения приходятся на одну и ту же строку → Следствие Запросы ждут, пока получат блокировку → На экране Сбои обменов, «Повторите попытку позже», таймауты
Основной ответственный Команда разработки · Разработка сервера · Совместно Команда инфраструктуры · Инфраструктура БД
Команда разработки: задачи
Разделить строку на несколько (шардированный счётчик), сделать транзакции короче, накапливать изменения в памяти и применять одним разом.
Команда инфраструктуры: задачи
Мониторить время и число ожиданий блокировок строк, находить строки, за которые идёт борьба, и сообщать о них команде разработки.
Цифры для ориентира
Если один запрос держит блокировку 10 ms, строку можно изменить не больше 100 раз в секунду. Если внутри транзакции есть обращение к другому серверу и обратно, это число падает ещё сильнее.
На графике
Растёт вслед за онлайном и нагрузкой · число и время ожиданий блокировок строк
Где смотреть
В MySQL смотреть прирост Innodb_row_lock_waits и Innodb_row_lock_time и значение Innodb_row_lock_current_waits, а через sys.innodb_lock_waits выяснять, кто кого ждёт. В PostgreSQL смотреть сессии, у которых wait_event_type равен Lock, в pg_stat_activity и запросы, у которых granted равно false, в pg_locks, а если включить log_lock_waits (по умолчанию выключен), долгие ожидания блокировок попадают в лог
Подтверждает
Ожидания блокировок резко растут вслед за событием и онлайном, и большинство ждущих запросов указывает на одну и ту же строку (один ключ) одной таблицы
Опровергает
Если ожидания равномерно распределены по многим таблицам и строкам, дело в насыщении диска или CPU. Если одна сессия долго держит блокировку и не отпускает её, это долго открытая транзакция (db-long-tx)
Чем проверить
Инструменты инфраструктуры (игровой код не нужен)
Источники
InnoDB LockingMySQL Когда транзакция ставит блокировку на строку (запись индекса), другие транзакции не могут изменить эту строку и ждут
How to Minimize and Handle DeadlocksMySQL Рекомендация держать транзакции маленькими и короткими и коммитить сразу после связанных изменений, чтобы уменьшить конфликты
Server Status VariablesMySQL Innodb_row_lock_waits и Innodb_row_lock_time показывают число и время ожиданий блокировок строк, Innodb_row_lock_current_waits показывает, сколько запросов ждут сейчас