Как избежать блокировок всей таблицы при удалении большого количества записей в 1С?

Системный администратор 1С v8.3 (Управляемые формы)
← На главную

В высоконагруженных системах на базе 1С:Предприятие часто возникает ситуация, когда удаление или изменение большого объема данных (например, очистка регистра сведений или проведение крупной накладной с тысячами строк) приводит к «замиранию» всей базы. Причиной тому является механизм эскалации блокировок в MS SQL Server. В этой статье мы подробно разберем, почему это происходит, как диагностировать проблему и какими способами можно предотвратить блокировку всей таблицы, сохранив при этом параллельную работу пользователей.

Почему происходит эскалация блокировок?

Рассмотрим внутренние механизмы SQL Server. Для управления транзакциями сервер использует блокировки разных уровней: строки (RID или KEY), страницы (PAG) или всей таблицы (TAB). По умолчанию SQL Server старается использовать блокировки минимального уровня (строки), чтобы обеспечить максимальную параллельность. Однако каждая блокировка потребляет память (примерно 96 байт на одну блокировку).

Проанализируем ситуацию: когда количество блокировок в рамках одной инструкции DELETE или UPDATE превышает определенный порог (по умолчанию это 5000 блокировок), SQL Server решает сэкономить память и заменяет тысячи мелких блокировок строк одной монопольной блокировкой всей таблицы (X-lock). С этого момента ни один другой пользователь не может даже прочитать данные из этой таблицы (если не используется RCSI) или изменить другие, совершенно не связанные строки.

Важно понимать нюанс с индексами. Если у вашей таблицы есть один кластерный индекс и два некластерных, то удаление одной строки порождает 3 блокировки (по одной в каждом индексе). Таким образом, фактическая эскалация может произойти уже при удалении ~1700 записей (1700 * 3 > 5000).

Диагностика проблемы

Выясним, как понять, что вы столкнулись именно с эскалацией. Основные признаки:

  1. В консоли кластера 1С или через SQL Activity Monitor видны ожидания на ресурсе LCK_M_X (exclusive lock) на уровне таблицы (OBJECT).
  2. В SQL Server Profiler или Extended Events фиксируется событие Lock:Escalation.
  3. Проблема проявляется только на «крупных» документах или операциях, в то время как мелкие отрабатывают мгновенно и параллельно.

Способ 1. Использование флагов трассировки (Глобальное решение)

Если изменить код 1С невозможно (например, это типовой механизм), наиболее эффективным методом будет использование флагов трассировки SQL Server. Разберем два основных флага, которые позволяют управлять эскалацией:

Флаг трассировки 1211: Полностью отключает эскалацию блокировок по количеству и по памяти. Это радикальное средство. Рассмотрим риск: если памяти под блокировки не хватит, SQL Server выдаст ошибку, и транзакция откатится. Однако на современных серверах с сотнями гигабайт оперативной памяти этот риск минимален.

Флаг трассировки 1224: Более «мягкий» вариант. Он отключает эскалацию по количеству (те самые 5000 строк), но оставляет возможность эскалации, если объем памяти под блокировки превысит 40% от выделенной памяти SQL Server. Это защищает систему от падения по памяти (Out of Memory).

Для включения флагов выполним команду в SQL Management Studio:


-- Включение флагов глобально для всего сервера
DBCC TRACEON (1211, -1);
DBCC TRACEON (1224, -1);

Внимание: Для того чтобы флаги работали после перезагрузки сервера, их необходимо прописать в параметрах запуска службы SQL Server (SQL Server Configuration Manager) как -T1211 и -T1224.

Способ 2. Настройка конкретной таблицы (Локальное решение)

Если мы не хотим влиять на весь сервер, можно попробовать отключить эскалацию только для проблемной таблицы. Проанализируем команду:

ALTER TABLE dbo._InfoRg6219 SET (LOCK_ESCALATION = DISABLE);

Это заставит SQL Server всегда использовать блокировки строк или страниц для данной таблицы. Однако здесь есть критическая ловушка для администраторов 1С. Любое обновление конфигурации, связанное с изменением структуры этого регистра (реструктуризация), приведет к тому, что 1С пересоздаст таблицу в базе данных. В этот момент настройка LOCK_ESCALATION = DISABLE будет безвозвратно утеряна. Поэтому данный метод требует постоянного контроля после каждого обновления.

Способ 3. Оптимизация кода на стороне 1С (Рекомендуемый подход)

Посмотрим на ситуацию со стороны разработчика 1С. Эскалация происходит на уровне одной инструкции SQL. Если мы выполняем удаление набором записей, 1С формирует один массивный запрос DELETE. Чтобы избежать порога в 5000, мы можем разбивать обработку данных на части внутри одной транзакции.

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


// Пример порционного удаления в одной транзакции
НаборЗаписей = РегистрыСведений.ИмяРегистра.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);

// Если записей слишком много, мы можем удалять их не одним махом,
// а порциями по другим измерениям, если логика позволяет.
// Но в 1С чаще всего используется полная перезапись набора.

// Лайфхак для разработчика:
// Если вы используете прямой SQL или сложные обработки,
// делайте DELETE в цикле по 1000 строк:
// DELETE TOP (1000) FROM Table WHERE Condition

Важно помнить: если мы находимся внутри ОбработкаПроведения(), мы уже в транзакции. Разделение удаления на два вызова по 2500 строк вместо одного на 5000 предотвратит эскалацию, так как каждая отдельная команда SQL не превысит порог, а общая транзакция останется целостной.

Проблема памяти и производительности

Проанализируем последствия отключения эскалации. Почему Microsoft вообще включила этот механизм? Управление миллионами мелких блокировок нагружает процессор (CPU). Каждую блокировку нужно создать, поместить в хеш-таблицу, проверить на конфликты и затем удалить.

Если вы отключаете эскалацию (флаг 1211) на таблице, где планируется изменение 10 миллионов строк в одной транзакции, будьте готовы к следующим последствиям:

Поэтому оптимальным решением для систем с 1000+ пользователями считается сочетание флага 1224 (защита по памяти) и архитектурного разделения крупных транзакций на более мелкие порции там, где это допустимо бизнес-логикой.

Итоги и рекомендации

Подведем итог нашему исследованию проблемы эскалации блокировок:

  1. Диагностируйте точно: Убедитесь через профайлер, что блокировка таблицы вызвана именно эскалацией (событие 5000+ locks), а не просто длительным удержанием блокировки строки.
  2. Используйте флаг 1224: Это наиболее безопасный способ для «лечения» симптомов на продуктивной базе без риска падения сервера по памяти.
  3. Контролируйте реструктуризацию: Если вы используете ALTER TABLE, создайте регламент перепроверки этой настройки после обновлений 1С.
  4. Оптимизируйте алгоритмы: Старайтесь не допускать изменения более 2000-3000 строк в одной команде Записать() набора записей.

Соблюдение этих правил позволит вашему серверу эффективно утилизировать ресурсы и обеспечит комфортную параллельную работу пользователей даже при выполнении тяжелых регламентных операций.

← На главную