КАК было написано в предыдущей статье: Блокировки 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. Поля блокировки. Возможные поля управляемых блокировок зависят от выбранного пространства и описаны в справке к методу "УстановитьЗначение()" объекта "ЭлементБлокировки". Использовать поля блокировки необходимо таким образом, что бы исключить возможные избыточные блокировки.
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Остатки");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Остатки");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Склад", "Склад");
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЦеныТоваров");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЦеныТоваров");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
ЭлементБлокировки.УстановитьЗначение("Период", Дата);
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЦеныТоваров");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
ЭлементБлокировки.УстановитьЗначение("Период", Новый Диапазон('00010101', Дата));
Анализ управляемых блокировок
Управляемые блокировки можно анализировать с помощью технологического журнала (ТЖ).
С помощью события 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С:Підприємство сама (неявно) устанавливает управляемые блокировки. Анализ управляемых блокировок можно проводить с помощью технологического журнала.