---
metadata:
  - name: generator
    content: Diplodoc Platform v5.50.6
alternate:
  - https://ydb.tech/docs/en/troubleshooting/performance/queries/transaction-lock-invalidation.md
  - https://ydb.tech/docs/ru/troubleshooting/performance/queries/transaction-lock-invalidation.md
sourcePath: ru/core/troubleshooting/performance/queries/transaction-lock-invalidation.md
---
> **Documentation Index:** Fetch the complete configuration index at https://ydb.tech/docs/ru/llms.txt

# Инвалидация блокировок транзакций

**[Инвалидация блокировок транзакций](https://ydb.tech/docs/ru/concepts/glossary.md#tli)** (TLI) происходит, когда одна транзакция (нарушитель) записывает данные и ломает [оптимистичные блокировки](https://ydb.tech/docs/ru/concepts/glossary.md#optimistic-locking) другой транзакции (жертвы). Жертва обнаруживает это при коммите и получает ошибку `transaction locks invalidated`. Транзакцию-жертву необходимо повторить. Частые повторы замедляют приложение.

{% note info %}

YDB SDK предоставляет встроенный механизм повтора временных ошибок. Подробнее см. [Обработка ошибок](https://ydb.tech/docs/ru/reference/ydb-sdk/error_handling.md).

{% endnote %}

## Предотвращение конфликтов

- **Сокращайте длительность транзакций.** Чем дольше транзакция удерживает блокировки, тем выше вероятность конфликта. По возможности избегайте [интерактивных транзакций](https://ydb.tech/docs/ru/concepts/glossary.md#interactive-transaction): лучший подход — один YQL-запрос с `BEGIN;` и `COMMIT;` для чтения, изменения и коммита данных. Если интерактивные транзакции необходимы, выполняйте `COMMIT` в последнем запросе.

- **Сокращайте пересечение данных между транзакциями.** Чем меньше строк читает транзакция, тем меньше блокировок она удерживает и тем ниже вероятность конфликта. Избегайте чтения заведомо ненужных данных. Если несколько транзакций конкурируют за одни и те же строки — пересмотрите схему данных.

- **Используйте режимы транзакций только на чтение.** Транзакции в режиме [`Snapshot Read-Only`](https://ydb.tech/docs/ru/concepts/transactions.md#modes) читают данные из консистентного снапшота и не устанавливают оптимистичных блокировок — такая транзакция никогда не станет жертвой TLI. Если транзакция не изменяет данные, явно задавайте этот режим через SDK.

### Пример: конфликт транзакций при резервировании товара

В интернет-магазине на распродаже сотни покупателей одновременно покупают один товар. Транзакция покупки читает остаток, потом приложение рассчитывает скидки, ждёт подтверждения оплаты, и только после этого списывает:

```sql
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` устанавливает [оптимистичную блокировку](https://ydb.tech/docs/ru/concepts/glossary.md#optimistic-locking) на строку. Пока транзакция ожидает оплаты, другая транзакция успевает изменить ту же строку и закоммититься — блокировка ломается, и первая транзакция получает TLI при коммите.

Решение — сразу резервировать товар одной короткой транзакцией, а всё медленное (скидки, оплата, внешние сервисы) выполнять после:

```sql
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

<!-- The include is added to allow partial overrides in overlays  -->
<!-- source: ru/troubleshooting/performance/queries/_includes/transaction-lock-invalidation.md -->
1. Откройте панель мониторинга **[DB overview](https://ydb.tech/docs/ru/reference/observability/metrics/grafana-dashboards.md#dboverview)** в Grafana.

1. Проверьте, есть ли всплески количества ошибок на диаграмме **Transaction Lock Invalidation**.

    ![](_assets/transactions-locks-invalidation.png)

    Эта диаграмма отображает количество запросов в секунду, возвращаемых с ошибкой «transaction locks invalidated».
<!-- endsource: ru/troubleshooting/performance/queries/_includes/transaction-lock-invalidation.md -->

### Диагностика через логи TLI

Когда транзакция завершается с ошибкой TLI, сообщение об ошибке содержит идентификатор запроса-жертвы:

```text
Transaction locks invalidated. ... VictimQuerySpanId: 1111111111111111.
```

По этому `VictimQuerySpanId` можно найти в логах сервера полный контекст конфликта: какой запрос установил блокировки и какой их сломал. Подробнее о включении логирования, формате записей и корреляции событий, а также об утилите `find_tli_chain` для автоматического анализа логов см. в [Логирование TLI](https://ydb.tech/docs/ru/troubleshooting/performance/queries/tli-logging.md).

### Анализ через системные представления

Для анализа конфликтов блокировок доступны следующие системные представления:

#### Анализ на уровне запросов

Для выявления запросов с наибольшим числом конфликтов используйте системное представление [`.sys/query_metrics_one_minute`](https://ydb.tech/docs/ru/dev/system-views.md#query-metrics):

```sql
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`](https://ydb.tech/docs/ru/dev/system-views.md#partitions) — текущая статистика по партициям, содержит кумулятивное поле `LocksBroken`
* [`.sys/top_partitions_by_tli_one_minute`](https://ydb.tech/docs/ru/dev/system-views.md#top-tli-partitions) — топ-10 партиций с ненулевым числом сломанных блокировок за минутный интервал
* [`.sys/top_partitions_by_tli_one_hour`](https://ydb.tech/docs/ru/dev/system-views.md#top-tli-partitions) — топ-10 партиций с ненулевым числом сломанных блокировок за часовой интервал

Пример запроса для поиска партиций с наибольшим числом сломанных блокировок:

```sql
SELECT
    Path,
    SUM(LocksBroken) as TotalLocksBroken
FROM `.sys/partition_stats`
GROUP BY Path
ORDER BY TotalLocksBroken DESC
LIMIT 10;
```

Пример запроса для просмотра истории сломанных блокировок по партициям:

```sql
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;
```
