Как получить дедлок для экспериментов
Что проверяют
Понимаете ли вы механику взаимной блокировки настолько, чтобы воспроизвести её руками, а не только пересказать определение. Следом обычно спрашивают, как база обнаруживает дедлок, что происходит с проигравшей транзакцией и как не допустить такого в параллельных загрузках.
Как отвечать
Проще всего воспроизвести в PostgreSQL: две сессии psql и таблица accounts с двумя строками. Порядок шагов важен:
- шаг 1: первая сессия делает
BEGINиUPDATE accounts SET v = v + 1 WHERE id = 1; - шаг 2: вторая сессия делает
BEGINи обновляет строку сid = 2; - шаг 3: первая сессия пытается обновить
id = 2и повисает в ожидании; - шаг 4: вторая пытается обновить
id = 1и замыкает цикл.
Через deadlock_timeout (по умолчанию 1 секунда) PostgreSQL проверяет граф ожиданий, находит цикл и откатывает одну из транзакций с ошибкой deadlock detected и кодом 40P01. Вторая продолжает работу.
Что ещё стоит назвать:
- MySQL/InnoDB: тот же сценарий, ошибка 1213, разбор последнего дедлока в
SHOW ENGINE INNODB STATUS, а сinnodb_print_all_deadlocksвсе случаи пишутся в лог; - без базы: два потока Python и два
threading.Lock, захваченных в разном порядке, сtime.sleepмежду захватами. Здесь детектора нет, процесс просто зависнет; - наблюдение:
pg_locksиpg_stat_activity, в логах помогаетlog_lock_waits.
Для дата-инженера важен практический вывод: дедлоки чаще всего возникают при параллельных UPSERT или UPDATE пачками, когда воркеры трогают одни и те же ключи в разном порядке. Лечится сортировкой ключей внутри пачки, разделением работы так, чтобы ключи воркеров не пересекались, и повтором транзакции при 40P01.
Где спотыкаются
Запускают две транзакции «одновременно» и ждут дедлока. Без паузы между шагами одна транзакция успевает закончить раньше, и воспроизведение получается нестабильным.
Путают дедлок с долгой блокировкой. Если одна сессия просто ждёт другую, это ожидание, его ограничивает lock_timeout. Дедлок это цикл, который сам не разрешится.
Считают ошибку дедлока сбоем базы и глушат её. Это штатное поведение, и код загрузки должен уметь повторить откатившуюся транзакцию.