Инвалидация блокировок транзакций
Инвалидация блокировок транзакций (TLI) происходит, когда одна транзакция (нарушитель) записывает данные и ломает оптимистичные блокировки другой транзакции (жертвы). Жертва обнаруживает это при коммите и получает ошибку transaction locks invalidated. Транзакцию-жертву необходимо повторить. Частые повторы замедляют приложение.
Примечание
YDB SDK предоставляет встроенный механизм повтора временных ошибок. Подробнее см. Обработка ошибок.
Предотвращение конфликтов
-
Сокращайте длительность транзакций. Чем дольше транзакция удерживает блокировки, тем выше вероятность конфликта. По возможности избегайте интерактивных транзакций: лучший подход — один YQL-запрос с
BEGIN;иCOMMIT;для чтения, изменения и коммита данных. Если интерактивные транзакции необходимы, выполняйтеCOMMITв последнем запросе. -
Сокращайте пересечение данных между транзакциями. Чем меньше строк читает транзакция, тем меньше блокировок она удерживает и тем ниже вероятность конфликта. Избегайте чтения заведомо ненужных данных. Если несколько транзакций конкурируют за одни и те же строки — пересмотрите схему данных.
-
Используйте режимы транзакций только на чтение. Транзакции в режиме
Snapshot Read-Onlyчитают данные из консистентного снапшота и не устанавливают оптимистичных блокировок — такая транзакция никогда не станет жертвой TLI. Если транзакция не изменяет данные, явно задавайте этот режим через SDK.
Пример: конфликт транзакций при резервировании товара
В интернет-магазине на распродаже сотни покупателей одновременно покупают один товар. Транзакция покупки читает остаток, потом приложение рассчитывает скидки, ждёт подтверждения оплаты, и только после этого списывает:
SELECT available FROM stock WHERE sku = $sku AND warehouse_id = $warehouse_id;
-- приложение проверяет: available > 0
-- приложение рассчитывает скидки, ожидает оплаты ...
UPDATE stock SET available = available - 1 WHERE sku = $sku AND warehouse_id = $warehouse_id;
COMMIT;
SELECT устанавливает оптимистичную блокировку на строку. Пока транзакция ожидает оплаты, другая транзакция успевает изменить ту же строку и закоммититься — блокировка ломается, и первая транзакция получает TLI при коммите.
Решение — сразу резервировать товар одной короткой транзакцией, а всё медленное (скидки, оплата, внешние сервисы) выполнять после:
BEGIN;
UPDATE stock SET available = available - 1, reserved = reserved + 1
WHERE sku = $sku AND warehouse_id = $warehouse_id AND available > 0;
UPSERT INTO orders (order_id, sku, qty, status)
VALUES ($order_id, $sku, 1, "RESERVED");
COMMIT;
-- после: расчёт скидок, оплата, доставка
Если оплата прошла — отдельной транзакцией ставим status = "PAID". Если нет — возвращаем товар из reserved обратно в available и ставим "CANCELLED". Транзакция резерва выполняется быстро и минимизирует время удержания блокировки.
Диагностика
Мониторинг в Grafana
-
Откройте панель мониторинга DB overview в Grafana.
-
Проверьте, есть ли всплески количества ошибок на диаграмме Transaction Lock Invalidation.

Эта диаграмма отображает количество запросов в секунду, возвращаемых с ошибкой «transaction locks invalidated».
Диагностика через логи TLI
Когда транзакция завершается с ошибкой TLI, сообщение об ошибке содержит идентификатор запроса-жертвы:
Transaction locks invalidated. ... VictimQuerySpanId: 1111111111111111.
По этому VictimQuerySpanId можно найти в логах сервера полный контекст конфликта: какой запрос установил блокировки и какой их сломал. Подробнее о включении логирования, формате записей и корреляции событий, а также об утилите find_tli_chain для автоматического анализа логов см. в Логирование TLI.
Анализ через системные представления
Для анализа конфликтов блокировок доступны следующие системные представления:
Анализ на уровне запросов
Для выявления запросов с наибольшим числом конфликтов используйте системное представление .sys/query_metrics_one_minute:
SELECT QueryText, LocksBrokenAsBreaker, LocksBrokenAsVictim
FROM `.sys/query_metrics_one_minute`
WHERE LocksBrokenAsBreaker > 0 OR LocksBrokenAsVictim > 0
ORDER BY LocksBrokenAsBreaker + LocksBrokenAsVictim DESC;
| Колонка | Описание |
|---|---|
LocksBrokenAsBreaker |
Сколько раз этот запрос сломал чужие блокировки |
LocksBrokenAsVictim |
Сколько раз блокировки этого запроса были сняты |
Запросы с высоким LocksBrokenAsBreaker — нарушители: именно они вызывают откаты других транзакций. Запросы с высоким LocksBrokenAsVictim — жертвы.
Анализ на уровне партиций
Для анализа сломанных блокировок на уровне партиций таблиц используйте следующие системные представления:
.sys/partition_stats— текущая статистика по партициям, содержит кумулятивное полеLocksBroken.sys/top_partitions_by_tli_one_minute— топ-10 партиций с ненулевым числом сломанных блокировок за минутный интервал.sys/top_partitions_by_tli_one_hour— топ-10 партиций с ненулевым числом сломанных блокировок за часовой интервал
Пример запроса для поиска партиций с наибольшим числом сломанных блокировок:
SELECT
Path,
SUM(LocksBroken) as TotalLocksBroken
FROM `.sys/partition_stats`
GROUP BY Path
ORDER BY TotalLocksBroken DESC
LIMIT 10;
Пример запроса для просмотра истории сломанных блокировок по партициям:
SELECT
IntervalEnd,
LocksBroken,
Path
FROM `.sys/top_partitions_by_tli_one_hour`
WHERE IntervalEnd BETWEEN Timestamp("2000-01-01T00:00:00Z") AND Timestamp("2099-12-31T00:00:00Z")
ORDER BY IntervalEnd DESC, LocksBroken DESC
LIMIT 100;