Каждый разработчик 1С рано или поздно сталкивается с критической ошибкой СУБД: «Microsoft SQL Server Native Client 11.0: Lock request time out period exceeded». Эта ошибка означает, что одна транзакция слишком долго ждала освобождения ресурсов, заблокированных другой транзакцией, и время ожидания блокировки (по умолчанию в 1С это 20 секунд) истекло. Давайте детально разберем физические причины возникновения этой проблемы, проанализируем поведение СУБД MS SQL Server и платформы 1С, а также рассмотрим пошаговые методы устранения конфликта блокировок.
В большинстве случаев проблема локализуется в коде обработчиков событий, выполняющихся в транзакции записи (например, в обработчике ПередЗаписью или ПриЗаписи). Когда в коде выполняется запрос к таблицам базы данных с ключевым словом ДЛЯ ИЗМЕНЕНИЯ, платформа транслирует этот запрос на уровень СУБД со специальными подсказками блокировки (такими как UPDLOCK). Это необходимо для того, чтобы гарантировать неизменность считываемых данных до момента завершения текущей транзакции.
Однако если база данных работает в автоматическом режиме блокировок (что крайне характерно для старых или неоптимизированных конфигураций), СУБД использует очень строгие уровни изоляции транзакций — REPEATABLE READ или SERIALIZABLE. В таких условиях неверно спроектированный запрос или структура метаданных могут парализовать работу всей системы.
Проанализируем ключевые факторы, которые вызывают лавинообразный рост блокировок и приводят к ошибкам таймаута:
ГДЕ) используется измерение или реквизит регистра, для которого не включено индексирование, SQL Server не может быстро найти нужные строки с помощью операции Index Seek. Вместо этого он вынужден выполнять полное сканирование всей таблицы (Table Scan или Clustered Index Scan).ДЛЯ ИЗМЕНЕНИЯ СУБД устанавливает блокировку обновления (U-блокировку) на абсолютно каждую строку, которую она считывает в процессе поиска. Когда количество таких строчных блокировок превышает порог СУБД (обычно около 5000 блокировок), SQL Server выполняет эскалацию — заменяет тысячи мелких блокировок строк одной монопольной блокировкой всей таблицы (TABLOCK). В этот момент проведение любых других документов, пытающихся записать данные в этот регистр, полностью останавливается.ВЫБОР КОГДА ... ТОГДА или сложных условий с логическим ИЛИ в секции ГДЕ делает запрос не-SARGable. Это означает, что даже при наличии индексов оптимизатор SQL Server не сможет их применить, и результатом снова станет полное сканирование таблицы с последующей эскалацией блокировок.ДЛЯ ИЗМЕНЕНИЯ без явного указания конкретного объекта заблокирует абсолютно все таблицы, участвующие в запросе, включая справочники и вспомогательные регистры.---
Рассмотрим ситуацию, когда запрос обращается к регистру накопления с отбором по конкретному измерению (например, Проформа). Если у данного измерения в свойствах конфигуратора для параметра Индексировать установлено значение Не индексировать, мы гарантированно получим сканирование таблицы при поиске.
Для исправления этой ситуации выполним следующие действия в конфигураторе:
После включения индекса СУБД построит сбалансированное дерево индекса (B-Tree). При выполнении запроса SQL Server мгновенно перейдет к нужным записям (выполнит Index Seek) и наложит блокировки обновления исключительно на целевые строки, не затрагивая данные других пользователей.
---
Посмотрим на пример неоптимизированного запроса, который накладывает избыточные блокировки на базу данных:
ВЫБРАТЬ
РегистрВзаиморасчеты.Период,
РегистрВзаиморасчеты.Регистратор,
РегистрВзаиморасчеты.Сумма
ИЗ
РегистрНакопления.Взаиморасчеты КАК РегистрВзаиморасчеты
ГДЕ
РегистрВзаиморасчеты.Проформа = &Проформа
ДЛЯ ИЗМЕНЕНИЯ
Если этот запрос усложнить соединениями с другими таблицами, то заблокируются все связанные сущности. Чтобы этого избежать, мы должны явно указать СУБД, какую именно таблицу мы планируем изменять. Перепишем запрос правильно:
ВЫБРАТЬ
РегистрВзаиморасчеты.Период КАК Период,
РегистрВзаиморасчеты.Регистратор КАК Регистратор,
РегистрВзаиморасчеты.Сумма КАК Сумма
ИЗ
РегистрНакопления.Взаиморасчеты КАК РегистрВзаиморасчеты
ГДЕ
РегистрВзаиморасчеты.Проформа = &Проформа
ДЛЯ ИЗМЕНЕНИЯ
РегистрНакопления.Взаиморасчеты
В данном случае конструкция ДЛЯ ИЗМЕНЕНИЯ РегистрНакопления.Взаиморасчеты указывает платформе 1С, что блокировать нужно исключительно физическую таблицу этого регистра, оставляя другие таблицы открытыми для чтения и записи в параллельных сеансах.
---
Разберем распространенную ошибку, когда разработчики пытаются реализовать сложную бизнес-логику непосредственно внутри SQL-запроса с помощью условного оператора ВЫБОР в секции фильтрации. Рассмотрим пример такого неэффективного кода:
ВЫБРАТЬ
ТаблицаРегистра.Проформа,
ТаблицаРегистра.СуммаОборота
ИЗ
РегистрНакопления.Взаиморасчеты КАК ТаблицаРегистра
ГДЕ
ТаблицаРегистра.Проформа = &Проформа
И ВЫБОР
КОГДА ТаблицаРегистра.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход) ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ
ДЛЯ ИЗМЕНЕНИЯ
РегистрНакопления.Взаиморасчеты
Конструкция ВЫБОР...КОНЕЦ внутри секции ГДЕ заставляет СУБД выполнять вычисления для каждой строки таблицы. Для оптимизации запроса мы должны избавиться от условных операторов и заменить их на чистые логические выражения, которые легко сопоставляются с индексами СУБД:
ВЫБРАТЬ
ТаблицаРегистра.Проформа КАК Проформа,
ТаблицаРегистра.СуммаОборота КАК СуммаОборота
ИЗ
РегистрНакопления.Взаиморасчеты КАК ТаблицаРегистра
ГДЕ
ТаблицаРегистра.Проформа = &Проформа
И ТаблицаРегистра.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
ДЛЯ ИЗМЕНЕНИЯ
РегистрНакопления.Взаиморасчеты
Если условия действительно сложные и включают исключения по разным типам документов-регистраторов, рекомендуется разбивать такой запрос на несколько простых ветвей и объединять их с помощью оператора ОБЪЕДИНИТЬ ВСЕ. Это позволит СУБД построить эффективный план для каждого отдельного подзапроса.
---
Если оптимизация индексов и запросов не решает проблему полностью из-за высокой интенсивности параллельной работы пользователей, необходимо кардинально изменить архитектуру работы с транзакциями — перейти на управляемый режим блокировок.
Проанализируем, как этот переход меняет поведение системы:
READ COMMITTED. При этом читающие транзакции больше никогда не блокируются пишущими, так как они считывают согласованные версии данных из системной базы tempdb.Для реализации этого подхода переведем конфигурацию (или конкретный объект) в управляемый режим блокировок и опишем явную блокировку в коде на языке 1С перед выполнением запроса:
ЭлементБлокировки = Новый БлокировкаДанных;
ЭлементБлокировкиДанных = ЭлементБлокировки.Добавить("РегистрНакопления.Взаиморасчеты");
ЭлементБлокировкиДанных.УстановитьЗначение("Проформа", ПараметрПроформа);
ЭлементБлокировкиДанных.Режим = РежимБлокировкиДанных.Исключительный;
// Активируем блокировку на время транзакции
ЭлементБлокировки.Заблокировать();
// Теперь можно безопасно выполнять запрос без ключевого слова ДЛЯ ИЗМЕНЕНИЯ
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Взаиморасчеты.Сумма КАК Сумма
|ИЗ
| РегистрНакопления.Взаиморасчеты КАК Взаиморасчеты
|ГДЕ
| Взаиморасчеты.Проформа = &Проформа";
Запрос.УстановитьПараметр("Проформа", ПараметрПроформа);
РезультатЗапроса = Запрос.Выполнить();
Использование объекта БлокировкаДанных гарантирует, что параллельные сеансы, пытающиеся провести документы по этой же проформе, будут ожидать своей очереди на уровне сервера приложений 1С, не нагружая и не блокируя таблицы MS SQL Server.
---
Если локализовать точную причину конфликта в коде 1С не удается, настроим диагностику на стороне СУБД с помощью механизма расширенных событий (Extended Events) и отчетов о заблокированных процессах (Blocked Process Report).
Разберем по шагам процесс настройки мониторинга на MS SQL Server:
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'blocked process threshold', 5; -- порог в секундах
RECONFIGURE;
blocked_process_report. Это событие генерирует подробный XML-отчет, содержащий информацию о блокирующем запросе (голова блокировки) и жертве блокировки (запрос, который упал по таймауту).<blocked-process> и <blocking-process>. Там будут указаны конкретные физические таблицы СУБД (например, _AccumReg12345), их индексы и тексты SQL-запросов, сгенерированные платформой 1С.Применяя данные методы комплексно — от базового индексирования до перехода на управляемые блокировки и детального анализа SQL-планов — мы сможем полностью исключить возникновение конфликтов блокировок и обеспечить стабильную и быструю многопользовательскую работу системы 1С.