При одновременной работе нескольких пользователей в клиент-серверном варианте базы 1С на СУБД Microsoft SQL Server нередко возникает ошибка: Конфликт блокировок при выполнении транзакции: Microsoft SQL Server Native Client: Превышено время ожидания запроса на блокировку (native=1222). Разберем механизм появления этой ошибки, выясним причины и рассмотрим по шагам методы её устранения.
Проанализируем ситуацию, происходящую внутри транзакции проведения документа. Когда транзакция открывается, платформа взаимодействует с MS SQL Server на определенном уровне изоляции транзакций:
ОбработкаПроведения выполняется запрос к табличной части документа или регистру без явного отбора по текущему документу (например, ВЫБРАТЬ Документы.Ссылка ИЗ Документ.ИмяДокумента.ИмяТабличнойЧасти КАК Документы), СУБД выполняет полное сканирование таблицы (Table Scan) или индекса (Index Scan).S) блокировки на уровне СУБД. Эти блокировки удерживаются до момента завершения транзакции (COMMIT или ROLLBACK).X) блокировку. Так как S и X блокировки несовместимы, второй сеанс переходит в состояние ожидания. Если первая транзакция удерживает блокировку дольше установленного таймаута (обычно от 20 до 40 секунд), второй сеанс падает с ошибкой превышения времени ожидания.Посмотрим на пример из практики: при отладке первого документа с точкой останова внутри процедуры ОбработкаПроведения транзакция остается открытой. Попытка провести второй документ в соседнем сеансе приводит к зависанию запроса и ошибке таймаута. При этом выполнение того же запроса в консоли запросов срабатывает мгновенно, так как консоль по умолчанию читает данные вне транзакции (с уровнем READ UNCOMMITTED или с хинтом NOLOCK).
Выясним причину сканирования всей таблицы. Зачастую в коде модуля документа по ошибке считываются строки без фильтрации по конкретному документу. Если нам необходимо получить данные табличной части проводимого документа, исключим сканирование всей таблицы.
Рассмотрим правильный вариант передачи ссылки текущего документа в параметрах запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТабЧасть.Номенклатура КАК Номенклатура,
| ТабЧасть.Количество КАК Количество
|ИЗ
| Документ.РегистрацияПотребленияУслуг.ПотреблениеУслугПоОбъектам КАК ТабЧасть
|ГДЕ
| ТабЧасть.Ссылка = &ТекущийДокумент";
Запрос.УстановитьПараметр("ТекущийДокумент", ЭтотОбъект.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Кроме того, если данные табличной части уже загружены в память объекта при проведении, обратимся к ним напрямую через коллекцию ЭтотОбъект.ПотреблениеУслугПоОбъектам, полностью исключив обращение к СУБД через тяжелые SQL-запросы.
Разберем фундаментальное архитектурное решение платформы 1С:Предприятие 8.3 — использование режима Управляемых блокировок в связке с версионированием строк на стороне MS SQL Server.
READ_COMMITTED_SNAPSHOT. Для этого выполним в SQL Server Management Studio следующий скрипт:
ALTER DATABASE [ИмяБазыДанных]
SET READ_COMMITTED_SNAPSHOT ON
WITH ROLLBACK IMMEDIATE;
В таком режиме операции чтения в транзакциях перестают накладывать разделяемые S-блокировки на уровне SQL Server. Читающие сеансы получают зафиксированные версии строк из базы данных tempdb, благодаря чему читающие транзакции не блокируют пишущие, а пишущие транзакции не блокируют читающие.
При переводе на управляемый режим контроль логической целостности (например, остатков на складах или взаиморасчетов) возлагается на разработчика. Перед чтением остатков в транзакции проведения установим явную блокировку только на контролируемые пространства измерений с помощью объекта БлокировкаДанных.
Посмотрим на пример использования объекта БлокировкаДанных:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиНоменклатуры");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = ЭтотОбъект.Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
ЭлементБлокировки.УстановитьЗначение("Склад", ЭтотОбъект.Склад);
Блокировка.Заблокировать();
В этом случае платформа блокирует не всю таблицу или диапазон страниц, а только конкретные комбинации номенклатуры и склада на уровне менеджера блокировок 1С, позволяя остальным пользователям параллельно и без задержек проводить документы по другим позициям и складам.
Проанализируем время удержания транзакций. Чем дольше выполняется процедура ОбработкаПроведения, тем выше вероятность конфликта. Применим следующие рекомендации:
ПередЗаписью до открытия транзакции проведения.Движения.ИмяРегистра.Записывать = Истина.