Почему 1С встаёт колом при проведении: эскалация блокировок на SQL Server
Содержание
Эскалация блокировок — это когда СУБД сама меняет много мелких блокировок на одну крупную. Было 6000 блокировок отдельных строк, стала одна блокировка всей таблицы. Для SQL Server это экономия памяти, для 1С — остановка: пока один пользователь проводит большой документ, остальные ждут и получают Превышено время ожидания при выполнении транзакции.
Эскалация блокировок в 1С возникает в клиент-серверном варианте на MS SQL Server. В файловой базе такого механизма нет, у PostgreSQL его тоже нет: там блокировка строки не превращается в блокировку таблицы. Всё ниже относится к связке 1С + SQL Server.
Лечат её тремя способами: не давать транзакции набирать тысячи блокировок, переключить базу на управляемые блокировки и, как крайняя мера, запретить эскалацию на конкретной таблице. Разберём по порядку.
Как работает эскалация блокировок в SQL Server
SQL Server умеет блокировать ресурсы на трёх уровнях: строка, страница (8 КБ), таблица целиком. Каждая блокировка стоит памяти, поэтому сервер не хочет держать их десятки тысяч.
Эскалацию запускают два условия:
- 🔒 один оператор (один запрос) взял больше 5000 блокировок на одну таблицу или индекс;
- 🧠 блокировки занимают слишком много памяти сервера: порядка 24% от памяти, которую выделили под механизм блокировок.
Первое условие срабатывает в 1С чаще всего. Проведение документа на 8000 строк, перепроведение периода, закрытие месяца : любая операция, которая за один запрос трогает много строк одной таблицы.
Сервер пробует превратить блокировки в одну блокировку таблицы. Если на таблице уже стоят чужие несовместимые блокировки, попытка не удаётся, и он повторяет её после каждых примерно 1250 новых блокировок. Поэтому эскалация нередко «догоняет» транзакцию уже в середине проведения.
После эскалации таблица заблокирована целиком: либо для записи (все остальные ждут), либо даже для чтения, если эскалировалась исключающая блокировка.
Не путайте с взаимной блокировкой (deadlock). Здесь никто не ждёт друг друга по кругу. Один сеанс просто занял всю таблицу, и остальные честно стоят в очереди.
Как понять, что 1С тормозит из-за эскалации
Сама по себе медленная база ещё ничего не значит. Эскалацию выдаёт набор признаков:
- ⏳ тормозит не всё, а работа с конкретными документами или регистрами: например, пока кто-то проводит реализацию, никто не может открыть остатки;
- 📉 пользователь А долго проводит большой документ, пользователь Б при этом не может провести мелкий, хотя они работают с разными контрагентами и складами;
- ❌ сообщения
Превышено время ожидания блокировки при выполнении транзакциииКонфликт блокировок при выполнении транзакции; - 🔁 проблема воспроизводится в часы закрытия периода, при массовых операциях и регламентных заданиях, а ночью её нет;
- 🧱 загрузка процессора на сервере низкая, диски тоже спокойны, а сеансы стоят. Это ожидание, а не нехватка мощности.
Если картина похожа, переходите к проверке. Гадать не нужно: эскалацию можно увидеть.
Диагностика эскалации блокировок
Счётчики по индексам
Самая быстрая проверка. Запустите запрос в SQL Server Management Studio в контексте базы 1С:
SELECT OBJECT_NAME(object_id) AS table_name,
index_id,
index_lock_promotion_attempt_count AS attempts,
index_lock_promotion_count AS successes
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL)
WHERE index_lock_promotion_attempt_count > 0
ORDER BY index_lock_promotion_count DESC;
Таблицы в SQL Server называются по-своему (_Reference123, _AccumRg456), в 1С таких имён нет. Сопоставить их можно в консоли запросов 1С: метод ПолучитьСтруктуруХраненияБазыДанных() отдаёт соответствие метаданных и таблиц.
Счётчики копятся с момента запуска службы SQL Server, поэтому сравнивайте значения до и после проблемного периода.
Расширенные события
Чтобы увидеть, какой именно запрос вызвал эскалацию, создайте сессию Extended Events на событие lock_escalation. В ней стоит собирать поля sql_text, database_name, object_id, session_id. Через час работы вы получите список запросов-виновников.
Сам по себе текст SQL-запроса читать неудобно. Его связывают с кодом 1С через технологический журнал.
Технологический журнал 1С
В файле logcfg.xml включите события TLOCK, TTIMEOUT, TDEADLOCK и DBMSSQL с фильтром по длительности. Что искать:
| Событие | Что показывает |
|---|---|
TTIMEOUT | кто не дождался блокировки и на каком ресурсе |
TLOCK | кто и какой ресурс захватывал, с какой длительностью |
DBMSSQL | запрос к СУБД и стек вызова из кода 1С |
По стеку находите процедуру, которая тянет тысячи строк за одну транзакцию. Это и есть место, где чинят.
Журнал на нагруженном сервере разрастается за часы. Включайте его с отбором и коротким сроком хранения, а после диагностики возвращайте logcfg.xml в прежний вид.
index_lock_promotion_count и включите ТЖ с событием TTIMEOUT, чтобы увидеть ресурс.sys.dm_tran_locks.Основные причины эскалации блокировок в 1С
За годами практики причины сводятся к нескольким повторяющимся.
Слишком большая транзакция
Документ проводится в одной транзакции целиком. Если в нём 10 000 строк товаров, а движения идут в регистры остатков и себестоимости, одна транзакция успевает набрать десятки тысяч блокировок.
Типичные источники:
- 📦 массовые документы с большими табличными частями (поступление, реализация, перемещение);
- 🗓 групповое перепроведение за месяц в одном цикле с одной общей транзакцией;
- 🧮 регламентные операции: закрытие месяца, расчёт себестоимости, пересчёт итогов.
Автоматический режим блокировок
Если в свойствах конфигурации стоит режим «Автоматический», платформа работает с СУБД на повышенном уровне изоляции (Repeatable Read / Serializable). Такие транзакции удерживают блокировки на читаемых данных до конца, и число блокировок растёт быстрее. Эскалация наступает гораздо раньше, чем в режиме управляемых блокировок.
Режим находится в свойствах корня конфигурации. Если база поднималась давно, на старых типовых и самописных решениях он до сих пор может быть автоматическим.
Запросы читают больше, чем нужно
Эскалацию создают не только записи. Запрос без отбора по индексированному полю читает таблицу целиком, а на высоком уровне изоляции это тысячи блокировок. Типичный виновник: условие по неиндексированному полю или функция над полем в условии, из-за чего СУБД не использует индекс.
Устаревшая статистика и фрагментация индексов
Когда статистика неактуальна, оптимизатор выбирает сканирование вместо поиска по индексу. Сканирование забирает больше строк, а вместе с ними больше блокировок. Регламентное обслуживание (обновление статистики, перестроение индексов) снижает вероятность эскалации косвенно, зато стабильно.
Параллельные тяжёлые операции в одном регистре
Два тяжёлых проведения пишут в один регистр накопления. Каждое по отдельности укладывается в лимит, но вместе они упираются в чужие блокировки, и эскалация не отменяется: сервер пробует повторно после каждого очередного приращения.
Как убрать эскалацию блокировок: порядок действий
Идите от самого безопасного к самому радикальному.
1. Раздробить транзакцию
Это основной и самый правильный способ. Если операция может обойтись без единой общей транзакции, разбейте её на порции по 500–1000 строк: обработали порцию, зафиксировали, взяли следующую.
Для перепроведения периода это означает «одна транзакция на один документ», а не «одна на весь период». Для документов с огромной табличной частью задача сложнее: нужно менять код проведения, и это работа для программиста.
Практический ориентир: держите число блокировок на таблицу в одной транзакции заведомо ниже 5000. Запас нужен, потому что платформа и СУБД берут блокировки и на индексы.
2. Перейти на управляемые блокировки
Конфигурация должна работать в режиме «Управляемый» (или «Автоматический и управляемый»). Тогда платформа ставит блокировки на уровне своих объектов, а СУБД работает на уровне изоляции Read Committed. Блокировок СУБД становится кратно меньше.
Что нужно знать до переключения:
- 🧩 типовые конфигурации (Бухгалтерия предприятия 3.0, ЗУП 3.1, Управление торговлей 11) уже работают в управляемом режиме;
- 🛠 самописная или сильно доработанная конфигурация может опираться на автоматические блокировки: переключение требует проверки, не появится ли «грязное» чтение или потерянные обновления;
- 🧪 сначала тестовая копия базы, потом боевая.
Менять режим блокировок в конфигурации рабочей базы без тестирования нельзя. Код, который рассчитывал на автоматические блокировки, после переключения может начать читать данные, которые параллельно меняют другие сеансы.
3. Включить READ_COMMITTED_SNAPSHOT
Параметр базы SQL Server READ_COMMITTED_SNAPSHOT заставляет читающие запросы брать версии строк из хранилища версий, а не вставать в очередь за писателями. Читатели перестают конфликтовать с писателями.
Платформа 1С его поддерживает. Работает это только в связке с управляемыми блокировками. Цена: рост нагрузки на tempdb, поэтому диск под неё должен быть быстрым.
4. Запретить эскалацию на проблемной таблице
Когда все нужные таблицы известны, на конкретной можно отключить эскалацию:
ALTER TABLE dbo._AccumRg456 SET (LOCK_ESCALATION = DISABLE);
Имя таблицы подставьте из своей базы. Возможные значения параметра: TABLE (по умолчанию), AUTO (для секционированных таблиц), DISABLE.
Эскалация отключается для этой таблицы, а память под блокировки теперь растёт вместе с числом строк. Не отключайте эскалацию всей базе без причины. Если одна транзакция наберёт миллионы блокировок, сервер уйдёт в нехватку памяти, и тогда запросы будут обрываться.
Флаги трассировки 1211 и 1224, которые советуют в старых заметках, действуют на весь сервер. 1224 отключает эскалацию по числу блокировок (но не по нехватке памяти), 1211 отключает её полностью. Для рабочего сервера 1С это слишком грубый инструмент, лучше LOCK_ESCALATION на конкретной таблице.
5. Если не помогло
Если эскалации в счётчиках нет, или после правок блокировки остались, проверьте соседние причины:
- 🔍 долгая транзакция из-за внешнего вызова (обмен, веб-сервис) внутри неё: блокировки висят, пока тот отвечает;
- 🧮 запрос с неудачным планом: смотрите план выполнения и индексы в запросе, который нашли по ТЖ;
- 🖥 нехватка памяти на сервере СУБД: при её дефиците эскалация идёт по второму условию, и без роста памяти или ограничения других потребителей не поможет;
- 🔄 регламентные задания, которые запускаются одновременно в рабочее время: перенесите их на ночь.
Профилактика: настройка 1С и SQL Server
Чтобы эскалация не возвращалась, закрепите несколько привычек.
- ✅ Регламент обслуживания. Обновление статистики каждую ночь, перестроение или реорганизация индексов раз в неделю. Это снижает размер сканирований.
- ✅ Память SQL Server. Задайте максимальный объём (
max server memory) так, чтобы оставалось место ОС и службам 1С, если они на той же машине. SQL Server, которому не ограничили память, съедает всё и вытесняет остальное. - ✅ Правила в разработке. Запрос внутри транзакции только с отбором по индексируемым полям; чтение больших объёмов выносят за транзакцию.
- ✅ Массовые операции по расписанию. Закрытие месяца, пересчёт итогов и групповое перепроведение запускают в нерабочее время.
- ✅ Мониторинг. Раз в неделю смотрите счётчики эскалации, как в запросе выше. Рост числа успешных повышений на одной таблице уже сигнал.
Для общего контроля можно использовать журнал регистрации: записи о длительных проведениях подсказывают, где копится нагрузка раньше, чем придут жалобы.
Ошибки, которые чаще всего совершают с блокировками
Тут только то, что не вошло в шаги выше.
- Лечат симптом таймаутом. Увеличивают время ожидания блокировки в настройках. Пользователи ждут дольше, а причина остаётся.
- Перезапускают сервер СУБД как лекарство. Это сбросит счётчики и оборвёт все сеансы, а эскалация вернётся при первой же нагрузке. Перезапускать службу SQL Server можно только в согласованное окно: все пользователи будут отключены.
- Включают флаг 1211 на сервере с несколькими базами. Он действует на все базы, а не только на 1С.
- Урезают права в 1С вместо исправления кода. Блокировки от этого не уменьшаются.
- Делают всё одновременно. Если сразу сменить режим блокировок, включить
READ_COMMITTED_SNAPSHOTи отключить эскалацию, неясно, что помогло, а что вредит. Меняйте по одному параметру и замеряйте.
Частые вопросы
Эскалация блокировок есть в файловой базе 1С?
Бывает ли эскалация блокировок на PostgreSQL?
При каком количестве блокировок SQL Server запускает эскалацию?
Можно ли отключить эскалацию только для одной таблицы?
ALTER TABLE ... SET (LOCK_ESCALATION = DISABLE). Остальные таблицы будут работать как раньше. Применяйте её после того, как нашли точную таблицу, а не заранее.Чем эскалация отличается от взаимной блокировки?
Как связать таблицу SQL Server с объектом 1С?
ПолучитьСтруктуруХраненияБазыДанных(). Он возвращает соответствие имён таблиц и метаданных, и в списке видно, какому регистру или справочнику принадлежит _AccumRg456.