Заблокировать обмен в 1с

Обновлено: 06.07.2024

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

Разделяемая блокировка 1С устанавливается для того, чтобы данные не были изменены другими транзакциями. Исключительная блокировка, помимо этого, обеспечивает запрет не только изменения этих данных, но даже их чтения другими транзакциями, устанавливающими управляемые блокировки. Можно сказать, что исключительная управляемая блокировка является средством борьбы с конфликтами блокировок (deadlock) и может использоваться аналогично ключевому слову для изменения языка запросов (используется в режиме автоматических блокировок).

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

2. Проведение приходных и расходных накладных

Исключительная блокировка 1С в отличии от разделяемой носит большую временную нагрузку для системы, ведь при разделяемой одни и те же файлы данных 1С можно записать одномоментно. Например, при одновременном проведении приходных накладных нам не важен контроль остатков, т.к. движения итак плюсовые.

Но при проведении расходной накладной нам не обойтись без исключительной блокировки 1С для соблюдения достоверности данных. В момент проведения данные по остаткам не должны быть доступны другим транзакциям.

3. Установка блокировки в коде

В коде установка блокировки выглядит следующим образом:

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

При установке блокировки важно понимать, какой минимум блокируемых данных нам необходим, ведь в случае блокировки избыточного количества данных мы увеличиваем вероятность ожидания этих данных другими транзакциями данных.

Есть план обмен, работа с которым реализована следующим образом:

Функция ЗаполнитьСтруктуруИзмененийДляУзла_Справочники(УзелПланаОбмена, ИмяТаблицы)

Если Метаданные.Справочники.Найти(ИмяТаблицы) <> Неопределено Тогда
Если ЗначениеЗаполнено(УзелПланаОбмена) Тогда
Запрос = Новый Запрос;
Запрос.Текст ="ВЫБРАТЬ ПЕРВЫЕ 3000
| Изменения.Ссылка КАК Объект
|ИЗ
| Справочник." + ИмяТаблицы + ".Изменения КАК Изменения
|ГДЕ
| Изменения.Узел = &Узел";
Запрос.УстановитьПараметр("Узел", УзелПланаОбмена);

//
Если ДанныеРегистрации.Количество() > 0 Тогда

2) Происходит выгрузка сведений

3) Удаление из плана обмена успешно выгруженных данных (и не поменявшихся за момень выгрузки)

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ПланыОбмена.УдалитьРегистрациюИзменений(УзелПланаОбмена, Выборка.Объект);
КонецЦикла;

Иначе
РезультатФункции = Ложь;
КонецЕсли;
Иначе
РезультатФункции = Ложь;
КонецЕсли;

После запуска этого механизма стала возникать ошибка блокировки:

Ошибка при вызове метода контекста (ВыбратьИзменения): Конфликт блокировок при выполнении транзакции:
Microsoft SQL Server Native Client 11.0: Транзакция (идентификатор процесса 55) вызвала взаимоблокировку ресурсов блокировка с другим процессом и стала жертвой взаимоблокировки. Запустите транзакцию повторно.

Т.е. блокировку вызывает вот эта строчка:

Поиски в интернете показали, что Да "ПланыОбмена.ВыбратьИзменения" вызывает блокировку.

Но почему она длительная и мешает и как можно адекватно обойти ее - не понятно

Еще бы она не вызывала блокировку. Она же пишущая.
А вот почему другой процесс в нее упирается и какой именно процесс - вопрос.
Релиз, платформа, режим блокировок?

"какой именно процесс в нее упирается" - могу предположить .что механизм, который регистрирует изменения в узел.

Понятно, что она пишущая. Не понятно, почему она на столько "блокирующая".


Этот вопрос возник у нас на проекте по внедрению ЗУП2.5 с численностью 20000 и средним количеством одновременных пользовательских сессий 200.

1.jpg

Стали разбираться. Оказалось, мы столкнулись с эффектом «Избыточной блокировки». Обычно этот эффект появляется при параллельном проведении документов, во время него самым первым документом блокируется большой объем записей регистров на все время проведения документа. Эта блокировка задерживает проведение остальных документов, мешает параллельной работе пользователей и замедляет рабочий процесс. Вообще блокировка данных при проведении документов вещь полезная, она сохраняет целостность данных и гарантирует правильность выполнения расчетных алгоритмов. Но бывает так, что либо объем заблокированных данных чрезмерен, либо время блокировки слишком велико. В результате мы имеем многопользовательскую систему, которая по сути является однопользовательской: вместо параллельного проведения документов - последовательное.

Ошибки в 1С из-за блокировок

Пример необходимой блокировки в 1С

Представим такую ситуацию – есть два документа «Начисление зарплаты сотрудникам организаций», в которых указан одинаковый налоговый период, а на закладке НДФЛ указаны одинаковые сотрудники. Рассмотрим случай, когда блокировка вообще отсутствует. Если последовательно запускать расчет этих документов, то в первом сумма НДФЛ посчитается правильно, а во втором будет равна нулю, т.к. рассчитанный и фактически начисленный НДФЛ на момент проведения второго документа будут совпадать.

Но если запустить эти документы параллельно, то они одновременно начислят НДФЛ, не подозревая о существовании друг друга, и в результате налог удвоится. Если блокировка настроена верно, то первый документ, запущенный на долю секунды раньше второго, успеет первым прочитать и заблокировать данные о фактически исчисленном налоге в регистре «НДФЛ расчеты с бюджетом» по сотруднику Пушкину А.С. Из этого запроса будет видно, что фактический налог за январь пока не начислялся и значит надо выполнить движение по регистру. Блокировка будет отпущена только после завершения записи в регистр. Второй документ, дойдя до запроса чтения фактически начисленного налога будет поставлен системой на ожидание до тех пор, пока первый документ не закончит транзакцию проведения, после чего он прочитает в запросе, что налог уже начислен и движение по регистру выполнять не надо. Это необходимая блокировка.

Пример избыточной блокировки в 1С

А теперь представим другую ситуацию. При проведении документа выполняется запрос, который должен отобрать документы, в которых присутствует сотрудник из этого документа. Но запрос написан так, что сервер SQL вынужден находить нужные документы методом перебора. Для технических специалистов это означает, что вместо CLUSTERED INDEX SCAN выполняется TABLE SCAN, т.е. вместо сканирования таблицы индексов происходит сканирование самой таблицы. Причем в процессе перебора блокируются все записи, к которым прикоснулся запрос, даже те, в которых не присутствуют искомые сотрудники. И эта блокировка будет действовать до конца завершения проведения документа, что будет препятствовать параллельному проведению документов с другими сотрудниками. Это избыточная блокировка.

Как избавиться от избыточных блокировок в 1С

Лечение избыточных блокировок может идти двумя путями. Первый - это оптимизация запросов, выполняемых внутри транзакций и добавление необходимых табличных индексов в конфигураторе. Второй - это перевод выполнения SQL-запросов на более низкий уровень изоляции, когда при выполнении запросов записи в таблицах блокируются только на момент выполнения самого запроса, либо не блокируются вовсе. А необходимые блокировки устанавливаются средствами объекта «БлокировкаДанных» и выполняются на стороне сервера 1С.

Теперь немного теории про уровни изоляции на SQL сервере:

1. В автоматическом режиме в транзакциях используется уровень изоляции SERIALIZABLE. Этот уровень накладывает блокировки типа X (запрещает чтение и запись) до конца транзакции на все данные, которых коснулись запросы или произошла запись данных.

2. В управляемом режиме в транзакциях используется уровень изоляции ReadCommitted. Этот уровень на записанные данные также устанавливает блокировки типа X до конца транзакции. Но при выполнении запросов на данные накладывает блокировки типа S (запрещает запись и проверяет нет ли в этот момент параллельных записей), при завершении запроса блокировки снимаются не дожидаясь завершения транзакции.

3. Если база данных переведена в режим ReadCommitted SNAPSHOT, то в управляемом режиме при чтении данных не накладывается блокировка типа S, есть только блокировка типа X при записи.

Тоже самое чуть более подробно в таблице:



Обычно лечение начинают с понижения уровня изоляции, т.к. это не особо трудозатратно и дает быстрый результат. Достаточно перевести конфигурацию из «Автоматического» режима управления блокировкой данных в «Управляемый», и транзакции начнут выполняться на уровне изоляции типа ReadCommitted, вместо SERIALIZABLE или Repeatable Read.

Чтобы переключить базу данных в режим READ COMMITTED SNAPSHOT (RCSI) необходимо в «SQL Server Management Studio» в свойствах базы данных установить параметр "Is Read Committed Snapshot On" в значение "True":

2.jpg

В некоторых источниках предлагают установить параметр "Allow Snapshot Isolation" в значение "True", но в этом нет необходимости, т.к. это приведет к включению другого режима изоляции SNAPSHOT, который не поддерживается платформой 1С (На момент написания статьи релиз платформы 8.3.9).

Режим управления блокировкой данных задается для неявных транзакций, которые выполняются при записи или при проведении документов, т.е. внутри предопределенных процедур типа ПриЗаписи() или ОбработкаПроведения(). Но большинство «тяжелых» вычислений в типовой конфигурации ЗУП2.5 происходит при выполнении команды «Рассчитать». При этом в модуле объекта запускается процедура РассчитатьВсе(), внутри которой неоднократно повторяется конструкция НачатьТранзакцию() …ЗафиксироватьТранзакцию(). Это явно указанные транзакции, внутри которых происходит запись и очистка регистров и выполняются запросы. Нам необходимо убедиться, что при переключении конфигурации в управляемый режим в процедуре «РассчитатьВсе()» транзакции также начинают выполняться на уровне ReadCommitted.

Для этого проведем небольшой эксперимент:

• Запустим SQL Server Profiler.

• Запустим «NEW TRACE».

• Выполним подключение к серверу SQL.

• В окне «Trace Properties» на закладке «General» выберем «Use the template» = «Blank», а на закладке «Events Selections» раскроем группу «Stored Procedures» и выберем «RPC:Complited». По кнопке «Column Filters» укажем имя базы и длительность выполнения запросов более 1.


• Кнопку RUN пока нажимать не будем, т.к. нам надо сначала запустить базу данных в режиме отладки и остановить выполнение расчета документа «Начисление зарплаты сотрудникам организаций» перед выполнением самого массивного запроса. Например, это будет команда
«Результат = Запрос.ВыполнитьПакет();» в функции «ПолучитьДанныеНДФЛПоРегистратору» в общем модуле «ПроведениеРасчетов». Здесь происходит выполнение основного запроса для расчета НДФЛ. Поставим на ней точку останова отладчика и запустим расчет в документе.

· После того как отладчик остановится, нажмем кнопку RUN в Профайлере.

· Теперь сделаем один шаг в отладчике кнопкой F11. Когда запрос будет выполнен и отладчик перейдет на следующий шаг, остановим чтение Профайлера кнопкой «Pause Selected Trace».

· Теперь найдем самый длительный запрос по колонке Duration и внимательно изучим текст запроса. Если при обращении к реальной (а не временной) таблице после слова WITH стоит SERIALIZABLE, то мы имеем дело с автоматическим режимом блокировки. Если ничего не стоит – то с управляемым.

6.jpg
7.jpg

Если в хинте запроса (Hint – это параметр после слова WITH, позволяющий влиять на план выполнения запроса) не указан уровень изоляции, то выполняется уровень изоляции, установленный по умолчанию для текущей SQL-сессии. Определить уровень изоляции, действующий по умолчанию для текущих сессий можно в «SQL Server Management Studio» с помощью команды

SEL ECT CASE transaction_isolation_level

WHEN 0 THEN 'Unspecified'

WHEN 1 THEN 'ReadUncommitted'

WHEN 2 THEN 'ReadCommitted'

WHEN 3 THEN 'Repeatable'

WHEN 4 THEN 'SERIALIZABLE'

WHEN 5 THEN 'SNAPSHOT' END AS TRANSACTION_ISOLATION_LEVEL

FR OM sys.dm_exec_sessions

В управляемом режиме для всех сессий будет указан режим ReadCommitted.

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

Настройка управляемых блокировок – это тема для отдельной статьи. Вкратце скажу, что программно управляемые блокировки устанавливаются с помощью объекта «БлокировкаДанных». Сами управляемые блокировки работают уже не на уровне SQL сервера, как в случае с автоматическими блокировками, а на уровне сервера 1С. Для определения необходимых и достаточных управляемых блокировок надо понимать логику программы одновременно на уровне бизнес-процессов и на уровне архитектуры таблиц СУБД.

Так же на этом проекте мы столкнулись с эффектом «Эскалация блокировок», когда SQL сервер сам принимает решение, что надо укрупнить область наложения блокировок вплоть до блокировки целиком всей таблицы. В результате работа пользователей останавливается, и все ждут завершения проведения одного документа – виновника эскалации, либо когда по таймауту снимутся взаимные блокировки, либо произойдет перезагрузка сервера. В каких случаях возникает эскалация и как с этим бороться тоже тема для отдельной статьи.

Читайте также: