Одной из самых распространенных и неприятных проблем в многопользовательской работе в 1С:Предприятие 8.3 является ошибка "Конфликт блокировок при выполнении транзакции". Эта ситуация возникает, когда несколько сеансов одновременно пытаются получить доступ к одним и тем же данным, и система не может обеспечить целостность транзакции. В этой статье мы подробно разберем, почему возникают блокировки, как проанализировать их причины и какими программными методами можно решить эту проблему.
Прежде чем переходить к коду, давайте проанализируем ситуацию и выясним, почему система выдает ошибку. В 1С существует два основных механизма блокировок: автоматический и управляемый. В современных конфигурациях чаще всего используется управляемый режим, так как он позволяет более гибко контролировать захват ресурсов.
Основные причины конфликтов:
Рассмотрим основной способ решения проблемы — использование объекта БлокировкаДанных. Это позволяет нам явно указать системе, какие именно записи мы хотим заблокировать, не полагаясь на автоматические механизмы платформы.
Проанализируем пример кода, который часто применяется в модулях проведения документов. Представим, что нам нужно заблокировать остатки по определенному складу перед проверкой и списанием.
// Создаем объект блокировки
Блокировка = Новый БлокировкаДанных;
// Добавляем элемент блокировки для регистра накопления
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
// Устанавливаем режим блокировки: Исключительный (для записи) или Разделяемый (для чтения)
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
// Указываем конкретные измерения, по которым будет установлена блокировка
ЭлементБлокировки.УстановитьЗначение("Склад", СсылкаНаСклад);
ЭлементБлокировки.ИсточникДанных = СписокТоваров;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
// Выполняем блокировку
Блокировка.Заблокировать();
Используя этот метод, мы гарантируем, что другие транзакции не смогут изменить остатки именно по нашему складу и списку товаров, пока наша транзакция не завершится. Это значительно снижает вероятность конфликта на уровне всей таблицы регистра.
Выясним причину, по которой даже при использовании управляемых блокировок могут возникать ошибки. Часто это связано с тем, что программисты устанавливают блокировки слишком поздно. Разберем по шагам правильный алгоритм проведения документа:
ОбработкаПроведения уже выполняется в транзакции.Блокировка.Заблокировать().Важный момент: всегда соблюдайте одинаковую последовательность обращения к таблицам во всех модулях системы. Если в одном документе сначала записывается "РегистрБухгалтерии", а потом "РегистрНакопления", то и во всех остальных документах порядок должен быть таким же.
Посмотрим на пример того, как сократить время удержания блокировки за счет использования пакетных запросов. Вместо того чтобы выполнять несколько отдельных обращений к базе данных, мы можем объединить их в один пакет. Проанализируем, как это помогает:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ.Номенклатура,
| ТЧ.Количество
|ПОМЕСТИТЬ ВтТовары
|ИЗ
| &ТЧ КАК ТЧ
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Остатки.Номенклатура,
| Остатки.КоличествоОстаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(
| &МоментВремени,
| Номенклатура В (ВЫБРАТЬ ВтТовары.Номенклатура ИЗ ВтТовары) И Склад = &Склад) КАК Остатки";
Запрос.УстановитьПараметр("ТЧ", Объект.Товары.Выгрузить());
Запрос.УстановитьПараметр("Склад", Объект.Склад);
Запрос.УстановитьПараметр("МоментВремени", Новый Граница(Объект.МоментВремени(), ВидГраницы.Исключая));
// Результат запроса получаем максимально быстро
РезультатЗапроса = Запрос.ВыполнитьПакет();
В данном случае мы используем Временные таблицы (ПОМЕСТИТЬ), что позволяет СУБД оптимально построить план запроса. Чем быстрее отработает запрос, тем быстрее освободятся ресурсы для других пользователей.
Если программная оптимизация не помогает, нам необходимо проанализировать ситуацию на уровне системы. Для этого настроим Технологический журнал (ТЖ). Нам интересны события TLOCK (управляемые блокировки) и TDEADLOCK (взаимные блокировки).
Рассмотрим структуру файла настроек logcfg.xml, который поможет выявить виновника блокировки:
<config xmlns="http://v8.1c.ru/7.4/config/logcfg">
<log location="C:\LOGS\LOCKS" history="24">
<event>
<eq property="Name" value="TLOCK"/>
</event>
<event>
<eq property="Name" value="TDEADLOCK"/>
</event>
<property name="all"/>
</log>
</config>
После сбора логов мы сможем увидеть, какой именно процесс (Context) удерживал блокировку и какой запрос вызвал ожидание. Это позволит нам точечно оптимизировать "узкие места" в коде 1С.
В завершение нашего разбора сформулируем несколько золотых правил, которые помогут избежать конфликтов блокировок:
Разделение итогов. Это позволит разным сеансам записывать движения в один и тот же регистр без блокировки друг друга на уровне итогов.ОбработкаПроведения.Мы проанализировали основные аспекты работы с блокировками в 1С 8.3. Применяя объект БлокировкаДанных, оптимизируя запросы и следуя правилам работы с транзакциями, вы сможете значительно повысить стабильность и скорость работы вашей системы в многопользовательском режиме.