한국어English日本語简体中文繁體中文DeutschไทยTiếng ViệtРусскийPortuguês (Brasil)EspañolBahasa Indonesia

Анатомия игровых лагов › L12 База данных

Конкуренция за блокировку горячей строки Hot row lock contention

ID причины db-hot-row · Основной ответственный Команда разработки · Разработка сервера · Совместно Команда инфраструктуры · Инфраструктура БД

Открыть карточку в основной версии с иллюстрациями и экспериментами →

Когда все пытаются изменить одну и ту же строку (гильдейский склад, популярный лот на аукционе, общий счётчик сервера), блокировку получает только один запрос за раз.

Почему Из-за события или популярного предмета все изменения приходятся на одну и ту же строку → Следствие Запросы ждут, пока получат блокировку → На экране Сбои обменов, «Повторите попытку позже», таймауты

Симптомы
Съеденные действия / роллбэк, Задержка ввода
Факторы
Остановка, Задержка
У кого
Только одна функция
Когда
При наплыве игроков
Ответственные
Основной ответственный Команда разработки · Разработка сервера · Совместно Команда инфраструктуры · Инфраструктура БД
Команда разработки: задачи
Разделить строку на несколько (шардированный счётчик), сделать транзакции короче, накапливать изменения в памяти и применять одним разом.
Команда инфраструктуры: задачи
Мониторить время и число ожиданий блокировок строк, находить строки, за которые идёт борьба, и сообщать о них команде разработки.
Цифры для ориентира
Если один запрос держит блокировку 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)
Чем проверить
Инструменты инфраструктуры (игровой код не нужен)

Источники

  1. InnoDB Locking MySQL
    Когда транзакция ставит блокировку на строку (запись индекса), другие транзакции не могут изменить эту строку и ждут
  2. How to Minimize and Handle Deadlocks MySQL
    Рекомендация держать транзакции маленькими и короткими и коммитить сразу после связанных изменений, чтобы уменьшить конфликты
  3. Server Status Variables MySQL
    Innodb_row_lock_waits и Innodb_row_lock_time показывают число и время ожиданий блокировок строк, Innodb_row_lock_current_waits показывает, сколько запросов ждут сейчас
  4. The innodb_lock_waits and x$innodb_lock_waits Views MySQL
    Ждущий запрос (waiting_query), блокирующая сессия (blocking_pid), время ожидания (wait_age)
  5. The Cumulative Statistics System (PostgreSQL Documentation) PostgreSQL
    wait_event_type в pg_stat_activity: значение Lock означает ожидание тяжёлой блокировки
  6. pg_locks (PostgreSQL Documentation) PostgreSQL
    granted равно false: процесс ждёт, чтобы получить блокировку
  7. Error Reporting and Logging (PostgreSQL Documentation) PostgreSQL
    log_lock_waits: пишет в лог ожидания блокировки дольше deadlock_timeout, по умолчанию выключен

Смотрите также

Тот же слой: L12 База данных

Причины с тем же симптомом (Съеденные действия / роллбэк) на других слоях

Карточка в основной версии с иллюстрациями и экспериментами