Управляемые блокировки 1С

КАК было написано в предыдущей статье: Блокировки 1С и MS SQL уровень изоляции READ_COMMITTED (используется в управляемом режиме) не решает проблем «Неповторяющееся чтение» и «Фантомное чтение». Что бы решить проблемы параллельного доступа «Неповторяющееся чтение» и «Фантомное чтение» необходимо использовать управляемые блокировки 1С. Механизм управляемых блокировок 1С позволяет решить проблемы согласованного и целостного чтения данных в транзакции.

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

Пример получения отрицательных остатков на складе из-за проблемы «Неповторяющееся чтение»:

Схема блокировок при неповторяющемся чтении
При чтении данных разделяемая блокировка СУБД накладывается только на время чтения (на время выполнения запроса). И может получиться такая ситуация:

Возникновение отрицательных остатков из-за проблемы неповторяющегося чтения
Короткая вторая транзакция успела выполниться, пока первая длительная еще не проверила остатки и не начала запись в регистр. Получаем отрицательные остатки. Выход — использование управляемых блокировок. Перед чтением (запросом) устанавливать исключительную управляемую блокировку 1С программно (вручную).
http://www.v8.1c.ru/overview/datalockcontrol.htm

Практические примеры решения проблем параллельного доступа с использованием управляемых блокировок описаны в статье «Блокировки в системе 1С: Эксперименты»

Менеджер управляемых блокировок платформы 1С

Непосредственно управлять (устанавливать, снимать) блокировками в СУБД возможности нет, поэтому платформа 1С имеет свой собственный менеджер управляемых блокировок, которым может управлять программист. Менеджер управляемых блокировок работает на сервере 1С (является одним из сервисов менеджера кластера 1С).

Важно понимать что управляемые блокировки 1С и транзакционные блокировки СУБД работают на разных уровнях трехзвенной клиент-серверной архитектуры:

Уровни клиент-серверной архитектуры
Управляемые блокировки, также как и транзакционные блокировки СУБД могут быть исключительными и разделяемыми. Исключительная запрещает и чтение и изменение заблокированного ресурса, а разделяемая запрещает только изменение (чтение возможно).

Пример программного кода установки управляемых блокировок:
//Контроль остатков
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Остатки");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
Блокировка.Заблокировать();
ТекстЗапроса = "ВЫБРАТЬ
| РеализацияТовары.Товар,
| РеализацияТовары.Количество,
| ОстаткиОстатки.КоличествоОстаток
|ИЗ
| Документ.Реализация.Товары КАК РеализацияТовары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Остатки.Остатки(&Период, Склад = &Склад)
| КАК ОстаткиОстатки
| ПО РеализацияТовары.Товар = ОстаткиОстатки.Товар
|ГДЕ
| РеализацияТовары.Ссылка = &Ссылка";
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.УстановитьПараметр("Период", МоментВремени());
Запрос.УстановитьПараметр("Склад", Склад);
Выборка = Запрос.Выполнить().Выбрать();

Управляемые блокировки всегда «живут» до конца транзакции. По окончании транзакции все установленные в ней управляемые блокировки снимаются платформой автоматически. Нет возможности снять управляемую блокировку программно. Попытка установки управляемой блокировки вне транзакции (или в транзакции с автоматическим режимом блокировок) приводит к исключению.

Возможна эскалация управляемых блокировок:  Количество установленных управляемых блокировок, превышение которого приводит к установке блокировки на все пространство блокировки, равно 100 000.

Неявные блокировки платформы

При записи любого объекта платформа сама неявно устанавливает исключительную управляемую блокировку на ссылку (для 8.3. еще и на поля блокировки), поэтому программно устанавливать управляемую блокировку непосредственно перед записью смысла не имеет. Неявно установленная платформой, управляемая блокировка при записи «живет» до конца транзакции.

При записи наборов платформа сама (неявно) устанавливает исключительную управляемую блокировку на значения измерений в наборе.
Если бы не было управляемых блокировок 1С, неявно накладываемых платформой при изменении объекта, то представим такую ситуацию: сеанс 1 блокирует ресурс (управляемая ручная блокировка), сеанс 2 спокойно записывает этот же ресурс, поскольку СУБД ничего не знает про нашу управляемую блокировку и блокировка сеанса 1 просто игнорируется.

Поведение неявной управляемой блокировки платформы при записи наборов регистров зависит от свойства набора «БлокироватьДляИзменения«. При установленном свойстве «БлокироватьДляИзменения» блокировка устанавливается без учета разделителя итогов (т. е. по всем разделителям). Такое действие запрещает параллельную запись в регистр из других сеансов, что гарантирует неизменность итогов регистра до конца транзакции. Свойство набора записей регистра «БлокироватьДляИзменения» появилось в релизе 8.2.13 специально для возможности реализации «Новой методики контроля отрицательных остатков при оперативном проведении».

При чтении наборов записей платформа сама (неявно) устанавливает разделяемую управляемую блокировку. Эта неявная блокировка сохраняется до конца транзакции (как все управляемые блокировки).

Где и как необходимо устанавливать управляемые блокировки

Где необходимо устанавливать управляемые блокировки:

  • В транзакциях с контролем остатков и последующим их изменением;
  • В транзакциях, где используется повторяющееся чтение одних и тех же данных из базы.

Как необходимо устанавливать управляемые блокировки:

1. Пространство блокировок. Возможные пространства управляемых блокировок описаны в справке к методу «Добавить()» объекта «БлокировкаДанных».

2. Режим блокировки. В транзакциях с контролем остатков и последующим их изменением необходимо устанавливать исключительную управляемую блокировку перед чтением данных. Это предотвратит возможность взаимоблокировки. В транзакциях, где используется повторяющееся чтение достаточно устанавливать разделяемую блокировку.

3. Поля блокировки. Возможные поля управляемых блокировок зависят от выбранного пространства и описаны в справке к методу «УстановитьЗначение()» объекта «ЭлементБлокировки». Использовать поля блокировки необходимо таким образом, что бы исключить возможные избыточные блокировки.

ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Остатки");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");

Управляемые блокировки 1С

ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Остатки");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Склад", "Склад");
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");

Управляемые блокировки 1С

ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЦеныТоваров");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");

Управляемые блокировки 1С

ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЦеныТоваров");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
ЭлементБлокировки.УстановитьЗначение("Период", Дата);

Управляемые блокировки 1С

ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЦеныТоваров");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
ЭлементБлокировки.УстановитьЗначение("Период", Новый Диапазон('00010101', Дата));

Управляемые блокировки 1С

Анализ управляемых блокировок

Управляемые блокировки можно анализировать с помощью технологического журнала (ТЖ).

С помощью события TLOCK технологического журнала можно анализировать установленные управляемые блокировки. Начиная с 8.2.14 в технологическом журнале для события TLOCK реализованы свойства Regions (список имен пространств блокировок), Locks (наложенные блокировки) и WaitConnections (список номеров соединений, с которыми идет столкновение).
Настраиваем ТЖ:
C:\Program Files\1cv8\conf\logcfg.xml:
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="C:\Program Files\1cv8\logs" history="24">
<event>
<eq property="Name" value="TLOCK"/>
</event>
<property name="All"/>
</log>
</config>

Смотрим технологический журнал сервера в C:\Program Files\1cv8\logs\rphost_XXX\

Неявные блокировки платформы при записи наборов:ависят от свойства «БлокироватьДляИзменения«:
Для случая БлокироватьДляИзменения=Ложь (блокировка итогов регистра с учетом разделителя итогов):
06:11.452026-3,TLOCK,5,process=rphost,p:processName=test83,t:clientID=84,t:applicationName=1CV8C,t:computerName=EREM-NB,t:connectID=10,SessionID=31,AppID=1CV8C,Regions=AccumRg68.DIMS,Locks='AccumRg68.DIMS Exclusive Period=[T"20140801000000":+] Splitter=5 Fld69=45:883a0013e8d1c89d11e42862577cbd3a Fld83=73:883b0013e8d1c89d11e4291e262fb158',WaitConnections=,Context=Форма.Записать : Документ.Реализация.Форма.ФормаДокумента

Для случая БлокироватьДляИзменения=Истина (блокировка итогов регистра без учета разделителя итогов):
06:11.499017-3,TLOCK,5,process=rphost,p:processName=test83,t:clientID=84,t:applicationName=1CV8C,t:computerName=EREM-NB,t:connectID=10,SessionID=31,AppID=1CV8C,Regions=AccumRg68.DIMS,Locks='AccumRg68.DIMS Exclusive Period=[T"20140801000000":+] Fld69=45:883a0013e8d1c89d11e42862577cbd3a Fld83=73:883b0013e8d1c89d11e4291e262fb158',WaitConnections=,Context='Форма.Записать : Документ.Реализация.Форма.ФормаДокумента

Установка явной программной исключительной блокировки на остатки регистра перед чтением:
06:11.468078-3,TLOCK,5,process=rphost,p:processName=test83,t:clientID=84,t:applicationName=1CV8C,t:computerName=EREM-NB,t:connectID=10,SessionID=31,AppID=1CV8C,Regions=AccumRg68.DIMS,Locks='AccumRg68.DIMS Exclusive Fld83=73:883b0013e8d1c89d11e4291e262fb158 Fld69=45:883a0013e8d1c89d11e42862577cbd3a',WaitConnections=,Context='Форма.Записать : Документ.Реализация.Форма.ФормаДокумента
Документ.Реализация.МодульОбъекта : 10 : Блокировка.Заблокировать();'

При использовании нескольких полей из источника данных:

ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Товар», «Товар»);
ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Склад», «Склад»);
Событие TLOCK показывает что блокирует пары измерений:
Fld69=45:884100059a3c780011e443164b633c76 Fld83=73:883b0013e8d1c89d11e4291e262fb158, Fld69=45:883a0013e8d1c89d11e42862577cbd3a Fld83=73:88420013e8d1c89d11e456b0b8dba916′,

Ошибки превышения ожидания установки управляемой блокировки можно анализировать с помощью события «TTIMEOUT»:

57:49.094000-0,TTIMEOUT,5,process=rphost,p:processName=test83,t:clientID=111,t:applicationName=1CV8C,t:computerName=EREM-NB,t:connectID=30,SessionID=99,AppID=1CV8C,WaitConnections=29,Context=’Форма.Записать : Документ.Реализация.Форма.ФормаДокумента

Документ.Реализация.МодульОбъекта : 10 : Блокировка.Заблокировать();’

Поле «WaitConnections» показывает соединение с сервером 1С, которое мешает установить блокировку. По нему можно определить SessionID (пользователя).

Также ошибку превышения ожидания управляемой блокировки фиксирует событие «EXCP»:

57:49.094001-0,EXCP,5,process=rphost,p:processName=test83,t:clientID=111,t:applicationName=1CV8C,t:computerName=EREM-NB,t:connectID=30,SessionID=99,AppID=1CV8C,Exception=DataBaseException,Descr=’Конфликтблокировокпривыполнениитранзакции:

Превышено максимальное время ожидания предоставления блокировки’,Context=’Форма.Записать : Документ.Реализация.Форма.ФормаДокумента

Документ.Реализация.МодульОбъекта : 10 : Блокировка.Заблокировать();’

Выводы

Платформа 1С имеет свой собственный менеджер блокировок, которыми может управлять программист. Поэтому такие блокировки называются «управялемыми» —  в отличии от транзакционных блокировками СУБД, которыми  нет возможности управлять непосредственно. Установка управляемых блокировок позволяет решить проблемы использования уровня изоляции READ_COMMITTED. При записи объектов платформа 1С сама (неявно) устанавливает управляемые блокировки. Анализ управляемых блокировок можно проводить с помощью технологического журнала.

Posted in Блог Оптимизации 1С.

4 Comments

  1. ЭлементБлокировки = Блокировка.Добавить(«РегистрНакопления.Остатки»);
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    ЭлементБлокировки.ИсточникДанных = Товары;
    ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Товар, Склад», «Товар, Склад»);

    Этот код не работает, накладывается исключительная блокировка на весь регистр без полей для отбора, т.к.
    ИспользоватьИзИсточникаДанных(, )

    Поле, а не Поля — проверено экспериментально, откуда эта теория?

    • Да, действительно с «ИспользоватьИзИсточникаДанных(«Товар, Склад», «Товар, Склад»)» в 8.2.14 и позже возникает ошибка… Вероятно так работало до 8.2.14 (В режиме совместимости 8.2.13 по крайней мере ошибки не возникает, но событие TLOCK не показывает поля). В режиме совместимости 8.2.16 правильно работает и такой код: ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Товар», «Товар»);
      ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Склад», «Склад»);
      TLOCK показывает что блокирует пары измерений:
      Fld69=45:884100059a3c780011e443164b633c76 Fld83=73:883b0013e8d1c89d11e4291e262fb158, Fld69=45:883a0013e8d1c89d11e42862577cbd3a Fld83=73:88420013e8d1c89d11e456b0b8dba916′,
      Спасибо за замечание, статью исправим. Теория от сюда например: http://devtrainingforum.v8.1c.ru/forum/thread.jsp?id=572655&print=1
      Извините, что долго не отвечали…

  2. «При чтении наборов записей платформа сама (неявно) устанавливает разделяемую управляемую блокировку»
    Это точно? Можете ткнуть где прочитать?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *