Блокировка 1С

Транзакция — это неделимая, с точки зрения воздействия на базу данных, последовательность операций манипулирования данными, выполняющаяся по принципу «все или ничего», и переводящая базу данных из одного целостного состояния в другое целостное состояние.

Если по каким-либо причинам одно из действий транзакции невыполнимо или произошло какое-либо нарушение работы системы, база данных возвращается в то состояние, которое было до начала транзакции (происходит откат транзакции).

Блокировка (в общем случае) — это информация о том, что данный ресурс «захвачен» кем-то для выполнения какого-то действия.

Суть блокировки:

  • запрет некоторых действий над общим ресурсом на некоторое ограниченное время.

В силу различных причин блокировки могут быть:

  1. необходимыми («хорошими») — благодаря им обеспечивается предсказуемость действий пользователей, целостность и непротиворечивость данных;
  2. избыточными («плохими»).

В системе 1С:Предприятие 8 различают:

  • объектные блокировки (позволяют осуществлять конкурентный доступ пользователей к данным 1С:Предприятия в терминах объектов информационной базы, например, при интерактивной работе пользователей в формах: редактировании существующих объектов, удалении, создании новых и др.);
  • транзакционные блокировки (обеспечивают конкурентный доступ к данным в целях целостности и непротиворечивости хранимых данных).

Содержание

Механизм объектных блокировок в 1С

Виды блокировок объектов базы данных 1С:

  1. объектная пессимистическая;
  2. объектная оптимистическая;

Объектная пессимистическая блокировка

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

При этом блокировка данных для редактирования не запрещает запись заблокированных данных в других пользовательских сеансах (или в других экземплярах объекта в этом же сеансе), а лишь не позволяет нескольким объектам одновременно установить блокировку одних и тех же данных.

Пессимистическая блокировка объектов базы данных:

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

Пример

Войдем в информационную базу под пользователем Иванов, откроем форму элемента 1С:Предприятие 8.0. Управление торговлей справочника Номенклатура (код 12) и изменим цену продажи с 420,00 на 450,00. Не сохраняя сделанные изменения, войдем в информационную базу еще раз, но теперь под именем пользователя Петров. Откроем форму того же элемента справочника и попробуем изменить значение какого-либо реквизита. Любая попытка изменения приведет к появлению специального окна с сообщением об ошибке:

Таким образом пессимистическая блокировка гарантирует, что пользователь, начав изменять данные объекта, сможет записать эти изменения в информационную базу.

Использование объектной пессимистической блокировки

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

  1. убедиться, не заблокирован ли он другими объектами;
  2. попытаться предотвратить его изменение другими пользовательскими сеансами (или другими экземплярами объекта в этом же сеансе).

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

Примеры

Пример № 1.

Требуется заблокировать объект и, если это удалось, модифицировать данные. В противном случае – проинформировать пользователя об отказе в выполнении операции с помощью сообщения вида:

1С (Код)

Аналогичным образом, можно воспользоваться методом глобального контекста ЗаблокироватьДанныеДляРедактирования:

1С (Код)

1 2 3 4 5 6 7 8 ФайлОбъект = ДанныеФайла.Ссылка.ПолучитьОбъект(); // Выполнить блокировку объекта от изменения другими режимами // или пользователями; в случае блокировки — // вывести пользователю сообщение об исключении. ЗаблокироватьДанныеДляРедактирования(ДанныеФайла.Ссылка); // Затем изменить и записать объект ФайлОбъект.Редактирует = Справочники.Пользователи.ПустаяСсылка(); ФайлОбъект.Записать();

Пример № 2.

Требуется пропустить обработку объекта, если он заблокирован для редактирования. При очередном вызове процедуры (например, из фонового или регламентного задания) будет предпринята повторная попытка изменения объекта.

1С (Код)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Объект = ТекущаяВерсия.ПолучитьОбъект(); // Выполнить блокировку объекта от изменения другими режимами // или пользователями УстановитьПолноеНаименование = Истина; Попытка ФайлОбъект.Заблокировать(); Исключение // в случае блокировки — не выполнять изменение объекта УстановитьПолноеНаименование = Ложь; // записать предупреждение в журнал регистрации ЗаписьЖурналаРегистрации(НСтр(«ru = ‘Фоновое обновление имен файлов'», Метаданные.ОсновнойЯзык.КодЯзыка), УровеньЖурналаРегистрации.Предупреждение,, ФайлОбъект, ОписаниеОшибки()); КонецПопытки; // Пропустить обработку объекта, если он заблокирован. Если УстановитьПолноеНаименование Тогда Объект.ПолноеНаименование = ПолноеНаименование; Объект.Записать(); КонецЕсли;

Не следует проверять блокировку объектов для редактирования в следующих случаях:

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

Для снятия пессимистической блокировки разработчик может использовать метод объекта Разблокировать(), причем использовать его для того же самого экземпляра объекта, для которого ранее была установлена блокировка.

ВАЖНО! Объектные блокировки не влияют на операции над данными и на процесс течения транзакций (они расположены на другом уровне работы с данными):

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

Объектная оптимистическая блокировка

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

Таким образом оптимистическая блокировка гарантирует, что пользователь изменяет актуальные данные объекта, которые хранятся в информационной базе, а не какой-то их предыдущий вариант.

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

Примеры

Рассмотрим пример. Откроем два сеанса работы с прилагаемой к работе информационной базой: один под пользователем Иванов, а другой — под пользователем Петров. В обоих сеансах откроем откроем форму элемента Управление торговлей справочника Номенклатура (код 12). Теперь в сеансе, открытом от имени пользователя Иванов, изменим цену продажи с 420,00 на 450,00 и запишем сделанные изменения. После этого, в сеансе, открытом от имени пользователя Петров попробуем изменить значение какого-либо реквизита. Любая попытка изменения приведет к появлению другого окна с сообщением об ошибке:

Механизм транзакционных блокировок

Возможные проблемы при многопользовательском доступе к одним и тем же данным

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

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

  1. Проблема потерянного изменения (англ. The Lost Update Problem) — если две транзакции изменяют одни и те же данные, взяв в качестве первоисточника начальное значение этих данных, то в системе останутся изменения внесенные той транзакцией, которая записала свои изменения последней, поскольку эти изменения заменят собой все изменения, внесенные до этого. Вывод:
    • нельзя одновременно изменять одни и те же данные.
  2. Проблема «грязного» чтения (англ. The Uncommitted Dependency Problem) — если одна транзакция начнет считывать некоторые данные не дождавшись окончания внесения изменений, вносимых в эти данные другой транзакцией, то достаточно вероятен случай, когда прочитанные данные будут содержать неверную информацию. Вывод:
    • нельзя читать уже измененные, но еще не записанные данные.
  3. Проблема неповторяемого чтения (англ. The Inconsistent Analysis Problem) — если одна транзакция несколько раз считывает одни и те же данные, а вторая — вносит изменения в эти данные между циклами чтения данных первой транзакции, то при повторном считывании первая транзакция может получить другой набор данных. Вывод:
    • нельзя повторно читать измененные и записанные данные, если эти же самые данные уже были прочитаны до внесения в них изменений.
  4. Проблема чтения фантомов (англ. The Phantom Read Problem) — если первая транзакция считывает данные и потом на их основе осуществляет определенные действия, а вторая транзакция в этот момент добавляет в эти данные новую информацию, то как и в предыдущем случае это может привести к некорректному результату. Вывод:
    • нельзя вводить новые данные (удалить имеющиеся), если они могут попасть в уже один раз прочитанные данные при повторном чтении.

Строго говоря, список вышеперечисленных проблем не является окончательным.

Уровни изоляции транзакций

Уровни изоляции транзакций в современных СУБД являются механизмом гибкой настройки ограничений, позволяющих:

  1. допустить параллельное выполнение транзакций ради увеличения производительности системы;
  2. обеспечить необходимую степень целостности данных (ограничить параллельность транзакций при работе с одними ресурсами).

Например, MS SQL Server 2000 позволяет использовать следующие уровни изоляции транзакции:

  • READ UNCOMMITED — незавершенное чтение. Низший уровень изоляции, обеспечивает максимальную параллельность выполнения транзакций. Данный уровень защищает изменяемые текущей (актуальной) транзакцией данные от изменений, которые могут внести конкурирующие транзакции. Если другой транзакции необходимо изменить те же самые данные, то она должна ожидать завершения изменения данных текущей (актуальной) транзакцией. Однако чтение данных разрешено. Таким образом этот уровень изоляции допускает чтение незавершенных изменений данных.
  • READ COMMITED — обеспечивает запрет «грязного» чтения. Если текущая (актуальная) транзакция начала изменять данные, то конкурирующая транзакция не может не только изменить, но даже прочитать их до завершения изменений, вносимых текущей (актуальной) транзакцией. После того, как изменения, вносимые текущей (актуальной) транзакцией закончены, конкурирующие транзакции могут читать данные, не дожидаясь окончания текущей (актуальной) транзакции в целом. Таким образом решается проблема неповторяемого чтения.
  • REPEATABLE READ — обеспечивает повторяемость чтения данных. Если текущая (актуальная) транзакция начинает читать данные, то другая транзакция не может их изменить до окончания текущей (актуальной) транзакции.
  • SERIALIZABLE — последовательное выполнение. Этот уровень изоляции является максимальным и обеспечивает полную изоляцию транзакций друг от друга. Решаются все рассмотренные проблемы, включая проблему «фантомов».

В зависимости от используемого уровня изоляции, СУБД накладывает различные типы блокировок на различные объекты базы данных на различное время.

Режим автоматических блокировок

В режиме автоматических блокировок 1С:Предприятие целиком «полагается» на возможности, предоставляемые СУБД.

Такой подход позволяет разработчику не задумываться о достаточно сложных вопросах блокирования нужных данных в транзакции. Однако СУБД не имеет информации о логической структуре данных 1С:Предприятия, и платформе приходится использовать достаточно высокие уровни изоляции транзакций СУБД для того, чтобы обеспечить целостность и непротиворечивость данных:

СУБД
Файловая база данных MS SQL Server IBM DB2 PostgreSQL
Вид блокировок Таблиц Записей Записей Таблиц
Уровень изоляции транзакций Serializable Repeatable Read или Serializable Serializable Read Committed

Режим автоматических блокировок:

  1. может привести к возникновению избыточных блокировок;

  2. не позволяет достичь желаемой параллельности работы пользователей.

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

Тип блокировок, устанавливаемых в том или ином случае, зависит от вида операции, используемого 1С:Предприятием уровня изоляции транзакций и определяется внутренними механизмами самой СУБД (например, MS SQL Server).

Режим управляемых блокировок

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

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

При работе в этом режиме система использует гораздо более низкий уровень изоляции транзакций для MS SQL Server и IBM DB2, и блокировку на уровне записей для PostgreSQL, что позволяет достичь более высокой параллельности работы пользователей:

СУБД
Файловая база данных MS SQL Server IBM DB2 PostgreSQL
Вид блокировок Таблиц Записей Записей Записей
Уровень изоляции транзакций Serializable Read Committed Read Committed Read Committed

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

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

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

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

Установка режима управления блокировками для объектов конфигурации

В структуре объектов конфигурации существует несколько возможностей для задания режима управления блокировками:

  1. через свойство «Режим управления блокировкой данных» в палитре свойств конфигурации и(или) объекта;
  2. установка управляемых блокировок средствами встроенного языка.

Установка режима управления блокировками через свойство «Режим управления блокировкой данных» в палитре свойств конфигурации или объекта

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

Сочетания режимов управления блокировками в транзакции в режиме Автоматический и управляемый:

Режим существующей транзакции Режим начинаемой транзакции Результат
Автоматический Автоматический Начинаемая транзакция будет выполнена в автоматическом режиме
Управляемый Управляемый Начинаемая транзакция будет выполнена в управляемом режиме
Автоматический Управляемый Начинаемая транзакция будет выполнена в автоматическом режиме
Управляемый Автоматический Будет вызвана исключительная ситуация

Особенности режимов управления блокировками для документов

Транзакция, единая для пользователя может представлять собой несколько транзакций с точки зрения платформы.

Интерактивное проведение документа по регистру делает две транзакции:

  1. запись самого документа;
  2. внутри этой записи (транзакции) запись набора строк по регистру.

В зависимости от режима управления блокировками для самого документа и двигаемого им регистра, возможны четыре ситуации:

  1. Режим документа Автоматический, режим регистра Автоматический -> запись по регистру в автоматическом режиме
  2. Режим документа Управляемый, режим регистра Управляемый-> запись по регистру в управляемом режиме
  3. Режим документа Автоматический, режим регистра Управляемый -> запись по регистру в автоматическом режиме
  4. Режим документа Управляемый, режим регистра Автоматический -> исключительная ситуация (ошибка)

Если, например, стоит задача повысить параллельность работы при проведении отдельного документа, не переводя при этом всю конфигурацию в управляемый режим, то последовательность действий должна быть следующей:

  1. свойство конфигурации Режим управления блокировкой данных необходимо установить в значение Автоматический и управляемый;
  2. свойство Режим управления блокировкой данных объекта метаданных документ необходимо установить в значение Управляемый;
  3. у всех регистров, по которым данный документ выполняет движения, следует установить свойство Режим управления блокировкой данных в значение Управляемый;
  4. проанализировать процедуру проведения документа на предмет наличия:
    • явных вызовов транзакций
    • неявных вызовов транзакций, которые выполняются системой при модификации данных каких-либо объектов конфигурации
  5. для найденных явных и неявных вызовов транзакций обеспечить их выполнение в управляемом режиме
    • для явных вызовов — параметр метода НачатьТранзакцию();
    • для неявных вызовов — свойство Режим управления блокировкой данных модифицируемого объекта конфигурации;
  6. в теле процедуры проведения документа установить необходимые управляемые блокировки.

Установка управляемых блокировок средствами встроенного языка

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

Описание доступных свойств и методов которого можно посмотреть в синтакс-помощнике в ветви «Общие объекты» — «Управление блокировкой данных».

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

При добавлении нового элемента блокировки для него необходимо указать пространство блокировок, которое будет блокировать данный элемент. Пространства блокировок определены в платформе 1С:Предприятия 8.1 и соответствуют структуре прикладных объектов конфигурации.

Допустимы следующие имена пространств блокировок и имена полей пространств блокировок:

  • для объектных данных (справочник, документ и др.) определено единственное пространство блокировки — сам объект данных;
  • для необъектных данных (например, регистры) определено по два пространства блокировок, которые имеют разный логический смысл.

Перечень допустимых имен

Пространство блокировок с суффиксом НаборЗаписей используется в тех случаях, когда необходимо заблокировать сами записи данного объекта (например, при добавлении новых записей).

Пространство блокировок без суффикса используется тогда, анализируются некоторые данные этого объекта (например, остатки регистра), или когда выполняются какие-либо операции, приводящие к изменению существующих данных объекта (например, восстановление границы последовательности).

Порядок действий при установке управляемых блокировок средствами встроенного языка:

  1. добавить элемент блокировки, соответствующий некоторому пространству блокировок;
  2. установить для этого элемента режим блокировки (разделяемая или исключительная);
  3. определить значения полей блокировки (указать, какие же именно записи будут заблокированы — для каждого пространства блокировок в платформе определены имена полей, значения которых могут задаваться при установке тех или иных блокировок).

ВНИМАНИЕ!

Следует понимать, что, в данном случае речь не идет о реальных записях базы данных. Несмотря на то, что управляемые блокировки описываются в терминах объектов метаданных и их полей, эти блокировки никак не связаны с реальной структурой хранения данных 1С:Предприятия в СУБД. Это всего лишь записи о том, что заблокировано «нечто».

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

Однако в других случаях провести такую аналогию достаточно затруднительно, да и не нужно. Например, при блокировке регистра бухгалтерии с указанием значения вида субконто, мы запрещаем другим транзакциям каким-либо образом изменять «записи» регистра бухгалтерии, у которых значение вида субконто равно указанному нами. Как при этом данное условие «проецируется» на реальную структуру данных регистра бухгалтерии — для нас совершенно не важно.

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

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

Для каждого пространства блокировок количество устанавливаемых условий не ограничено.

Способы задания условий на поля пространств блокировки

Существует два способа задания условий на поля пространств блокировки:

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

Задание условий на поля пространств блокировки с помощью явного задания имени поля и его значения

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

Листинг 1

Листинг 1. Пример установки условия блокировки записей с помощью явного указания имени поля и его значения

Для значений типа Дата или Число в качестве значения может быть задан некоторый диапазон значений. Диапазон значений передается методу с помощью объекта встроенного языка — Диапазон. Данный объект позволяет задать верхнюю и нижнюю границы диапазона, причем в диапазон включаются и границы диапазона (листинг 2).

Листинг 2

Листинг 2. Пример установки условия блокировки записей с помощью задания диапазона

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

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

Листинг 3

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

В качестве источника данных можно указывать:

  • результат запроса;
  • табличную часть;
  • набор записей;
  • таблицу значений.

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

Объект Диапазон также может являться значением поля источника данных.

Для установки всех созданных блокировок используется метод объекта БлокировкаДанных — Заблокировать():

  • Если метод выполняется внутри транзакции (явной или неявной), то блокировки устанавливаются в момент вызова метода. При окончании транзакции они будут сняты автоматически.
  • Если же метод Заблокировать() выполняется вне транзакции, то блокировки установлены не будут.

Управляемые блокировки в 1С 8.2. в простых и понятных примерах. Статья будет полезна тем, кто все еще не до конца понимает принципы работы управляемых блокировок и то, чем отличаются блокировки новой схемы проведения от старой. Для тех у кого есть доступ, так же советую ознакомится с исчерпывающей статьей от 1С про блокировки данных в 1С:Предприятии 8, которую можно прочитать здесь: http://kb.1c.ru/articleView.jsp?id=30

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

В ПН и РН есть одинаковые по структуре табличные части (ТЧ) СписокНоменклатуры, в которых содержится список Номенклатуры, Свойств, Количество, Сумма, Цена:

В Регистре накопления есть соответсвующие измерения: Номенклатура, Свойство и два ресурса — Сумма и Количество.

Предварительно для работы с управляемыми блокировками установим управляемый режим блокировок для конфигурации в целом и управляемывй режим блокировок отдельно для Приходной накладной, Расходной накладной и для всех регистров, движения по которым эти документы делают. В наших примерах — это один регистр накопления Остаток товара.

Обратим внимание, что в файловой версии можно устанавливать блокировки только на таблицы целиком (см. таблицу ниже), построчные (в разрезе выбранных движений из регистра накопления, например) блокировки в файловом режиме не работают. Примеры тестировались на 1С 8.2 + MS SQL 2008 R2 Express.

Детальнее про поддерживаемые режимы блокировок тут: http://v8.1c.ru/overview/Term_000000642.htm

Примеры рассматриваются на следующих РН и ПН:

ПН — 01

ПН — 02

РН — 01

Пример 1. Блокировка по конкретно известной номенклатурной позиции.

Код процедуры ОбработкаПроведения(Отказ, Режим) модуля объекта документа РасходнаяНакладная:

Движения.ОстаткиНоменклатуры.Записывать = Истина;

//Далее идет заполнение движений

Как это работает:

Если в процессе проведение РН — 01, сделать попытку провести ПН — 01, то она будет ожидать завершения блокировки регистра накопления по измерению «Номенклатура» с наименованием «Ботинки» и ПН — 02 будет ожидать завершения блокировки регистра накопления по измерению «Номенклатура» с наименованием «Ботинки».

Пример 2. Блокировка по конкретно известной паре номенклатурная позиция — свойство.

Код процедуры ОбработкаПроведения(Отказ, Режим) модуля объекта документа РасходнаяНакладная:

Как это работает:

Если в процессе проведение РН — 01, сделать попытку провести ПН — 01, то она будет ожидать завершения блокировки регистра накопления по паре измерений «Номенклатура» (с наименованием «Ботинки») -«Свойство» (с наименованием «черные 42 размера»), а ПН — 02 проведется без ожидания, так как в ее ТЧ СписокНоменклатуры нет пары «Номенклатура»-«Свойство», которая будет образовывать соответвствующее движение.

Пример 3. Блокировка на всю таблицу регистра накопления.

Код процедуры ОбработкаПроведения(Отказ, Режим) модуля объекта документа РасходнаяНакладная:

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

//Далее идет заполнение движений

Как это работает:

Если в процессе проведение РН — 01, сделать попытку провести ПН — 01, то она будет ожидать завершения блокировки таблицы регистра накопления и ПН — 02 будет ожидать завершения блокировки таблицы регистра накопления.

Пример 4. Блокировка по одному полю из источника данных для блокировки (это, например, список движений или список номенклатуры по которой будут движения).

Код процедуры ОбработкаПроведения(Отказ, Режим) модуля объекта документа РасходнаяНакладная:

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

//Далее идет заполнение движений

Как это работает:

Если в процессе проведение РН — 01, сделать попытку провести ПН — 01, то она будет ожидать завершения блокировки регистра накопления по измерению «Номенклатура» и значением «Ботинки» из источника СписокНоменклатуры и ПН — 02 будет ожидать завершения блокировки регистра накопления по измерению «Номенклатура» и значением «Ботинки» из источника СписокНоменклатуры (так как именно такое значение реквизита «Номенклатура» в переданной как источник ТЧ).

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

Код процедуры ОбработкаПроведения(Отказ, Режим) модуля объекта документа РасходнаяНакладная:

Блокировка = Новый БлокировкаДанных;

//Далее идет заполнение движений

Как это работает:

Если в процессе проведение РН — 01, сделать попытку провести ПН — 01, то она будет ожидать завершения блокировки регистра накопления по паре измерений «Номенклатура» значение «Ботинки» из переданного источника — «Свойство» значение «черные 42 размера» из переданного источника, а ПН — 02 проведется без ожидания, так как в ее ТЧ СписокНоменклатуры нет пары «Номенклатура»-«Свойство», которая будет образовывать соответвствующее значениям полей источника блокировки движение.

Пример 6. Блокировка по двум полям из источника данных для блокировки.

Код процедуры ОбработкаПроведения(Отказ, Режим) модуля объекта документа РасходнаяНакладная:

//условие «И» — будут заблокированы и записи для номенклатуры из ТЧ СписокНоменклатуры

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

//Далее идет заполнение движений

Как это работает:

Если в процессе проведение РН — 01, сделать попытку провести ПН — 01, то она будет ожидать завершения блокировки регистра накопления по измерению «Номенклатура» значение «Ботинки» с любыми свойствами и отдельно по «Свойству» со значением «черные 42 размера» для любой номенклатуры, в ПН — 01 есть и номенклатура «Ботинки» и номенклатура со свойствами «черные 42 размера». Аналогично ПН — 02 будет ожидать завершения блокировки, так как в ПН — 02 есть номенклатура «Ботинки».

Пример 7. Новая схема проведения (чем же отличается БлокироватьДляИзменеия = Истина от блокировок при помощи объекта БлокировкаДанных).

Код процедуры ОбработкаПроведения(Отказ, Режим) модуля объекта документа РасходнаяНакладная:

//Новая схема (без механизма контроля остатков)

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

Движение.Свойство = СтрокаН.Свойство;
КонецЦикла;

//1
Движения.ОстаткиНоменклатуры.БлокироватьДляИзменения = Истина;
Движения.ОстаткиНоменклатуры.Записать(); //Набор не пустой будет выполнена блокировка, источник — ТЧ СписокТоваров

//Далее должен идти механизм контроля остатков

Как это работает:

Если в процессе проведение РН — 01, сделать попытку провести ПН — 01, то она будет ожидать завершения блокировки регистра накопления по паре измерений «Номенклатура» значение «Ботинки» из переданного источника — «Свойство» значение «черные 42 размера» из переданного источника, а ПН — 02 проведется без ожидания, так как в ее ТЧ СписокНоменклатуры нет пары «Номенклатура»-«Свойство», которая будет образовывать соответвствующее значениям полей источника блокировки движение.

При использовании команды БлокироватьДляИзменения = Истина на регистр накопления по значениям измерений движений установится исключительная блокировка. Никаких конструкций с использованием объекта БлокировкаДанных для этого писать не нужно.

Код под комментарием //1 выше аналогичен следующему коду:

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

//Далее должен идти механизм контроля остатков

Привет, друзья! Сегодня мы поговорим про свойство регистра «Режим разделения итогов». Все слышали, что такое существует, но на практике мало кто знает – что это за зверь.

Итак, как обычно, есть проблема – большое время ожидания предоставления блокировки, ошибки таймаута блокировок:

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

И тут мы включаем режим разделения итогов регистра. Технически, это выглядит так:

Система добавила колонку в таблицу итогов «_Splitter», или разделитель. Теперь, когда несколько человек одновременно пишут данные по одному набору измерений, все строки попадают в таблицы итогов, но с разными значениями разделителя (0, 1, 2 …).

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

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

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

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

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

Чтобы этого не происходило, в тех местах, где используется метод «посмертного вскрытия» набору записей необходимо устанавливать свойство «БлокироватьДляИзменения». При этом установится управляемая блокировка на регистр по нашим измерениям, а также будет недопустимо использование режима разделения итогов в данной ситуации. Это и логично, мы как бы сообщаем системе – «мы не просто пишем данные, а хотим потом проверить остатки, и другие пользователи не должны изменить эти данные, пока мы не закончим».

Спасибо за внимание 🙂

Помогла ли вам данная статья?

1С:Предприятие 8.2 /
Разработчикам /
Реализация обработки данных

Использование чтения вне транзакции

См. также

1.1. Если чтение данных из информационной базы должно быть ответственным , следует производить такое чтение в транзакции с предварительной установкой управляемых блокировок. В общем случае, ответственным следует считать любое чтение, на основе результатов которого производятся какие-либо изменения в информационной базе или принимаются решения.
Например, ответственное чтение данных требуется в следующих случаях:

  • Чтение данных при проведении, для последующего формирования движений;
  • Чтение данных для последующей целостной передачи в другую систему, например в программы типа «Клиент банк «;
  • Выполнение групповой обработки объектов *
    * Примечание: перед модификацией ссылочных объектов, обычно, следует устанавливать на них пессимистичные объектные блокировки.

Неправильно:

// 1. Прочитать регистр сведений Запрос = Новый Запрос( «ВЫБРАТЬ РАЗРЕШЕННЫЕ | ЗаметкиПоПредмету.КоличествоЗаметок КАК КоличествоЗаметок |ИЗ | РегистрСведений.ЗаметкиПоПредмету КАК ЗаметкиПоПредмету |ГДЕ | ЗаметкиПоПредмету.Предмет = &Предмет»); Запрос.УстановитьПараметр(«Предмет», ПредметЗаметок); Выборка = Запрос.Выполнить().Выбрать(); КоличествоЗаметок = 0; Если Выборка.Следующий() Тогда КоличествоЗаметок = Выборка.КоличествоЗаметок; КонецЕсли; // 2. Записать в регистр сведений НаборЗаписей = РегистрыСведений.ЗаметкиПоПредмету.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Предмет.Установить(ПредметЗаметок); НоваяЗапись = НаборЗаписей.Добавить(); НоваяЗапись.Предмет = ПредметЗаметок; НоваяЗапись.КоличествоЗаметок = КоличествоЗаметок + 1; НаборЗаписей.Записать();

Правильно:

// 1. Начать транзакцию для пакета из двух операций чтения и записи регистра НачатьТранзакцию(); Попытка // 2. Установить исключительную блокировку на интересующий диапазон записей регистра, // для того чтобы гарантировать, что в момент записи количество заметок не изменилось с момента чтения в каком-либо другом сеансе. БлокировкаДанных = Новый БлокировкаДанных; ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(«РегистрСведений.ЗаметкиПоПредмету»); ЭлементБлокировкиДанных.УстановитьЗначение(«Предмет», ПредметЗаметок); ЭлементБлокировкиДанных.Режим = РежимБлокировкиДанных.Исключительный; БлокировкаДанных.Заблокировать(); // 3. Прочитать регистр сведений Запрос = Новый Запрос( «ВЫБРАТЬ РАЗРЕШЕННЫЕ | ЗаметкиПоПредмету.КоличествоЗаметок КАК КоличествоЗаметок |ИЗ | РегистрСведений.ЗаметкиПоПредмету КАК ЗаметкиПоПредмету |ГДЕ | ЗаметкиПоПредмету.Предмет = &Предмет»); Запрос.УстановитьПараметр(«Предмет», ПредметЗаметок); Выборка = Запрос.Выполнить().Выбрать(); КоличествоЗаметок = 0; Если Выборка.Следующий() Тогда КоличествоЗаметок = Выборка.КоличествоЗаметок; КонецЕсли; // 4. Записать в регистр сведений НаборЗаписей = РегистрыСведений.ЗаметкиПоПредмету.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Предмет.Установить(ПредметЗаметок); НоваяЗапись = НаборЗаписей.Добавить(); НоваяЗапись.Предмет = ПредметЗаметок; НоваяЗапись.КоличествоЗаметок = КоличествоЗаметок + 1; НаборЗаписей.Записать(); ЗафиксироватьТранзакцию(); Исключение // 5. Если при установке блокировки возникла исключительная ситуация из-за того, что регистр уже заблокирован в другом сеансе (или по другим причинам), // отменить транзакцию и записать сведения об ошибке в журнал регистрации. ОтменитьТранзакцию(); ЗаписьЖурналаРегистрации(НСтр(«ru = ‘Заметки'»), УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); ВызватьИсключение; КонецПопытки;

В некоторых случаях, ответственное чтение не требуется в силу решаемой прикладной задачи, например:

  • Получение данных динамическими списками;
  • Поиск данных;
  • Формирование большинства отчетов.

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

  • Обращение к условно постоянной информации. Например, чтение константы ВалютаРегламентированногоУчета или обращение к учетной политике;
  • Действия, которые гарантированно выполняются в монопольном режиме. Например, в процедурах обновления и первоначального заполнения данных информационной базы;
  • Действия над данными, доступ к которым имеет только один пользователь , поэтому конкурентная работа с ними маловероятна или полностью исключена.
    Например, персональные данные, хранящиеся в «разрезе» пользователей.

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

Например, в системной транзакции выполняются обработчики модулей объектов и соответствующие им подписки на события:

  • ПередЗаписью;
  • ПриЗаписи;
  • ПередУдалением.

Подробнее – см. документацию к платформе 1С:Предприятие .

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

Пример установки разделяемой блокировки (без открытия транзакции – в предположении, что ранее уже была открыта системная транзакция):

// 1. Установить разделяемую блокировку для ответственного чтения нескольких связанных объектов Блокировка = Новый БлокировкаДанных; ЭлементБлокировки = Блокировка.Добавить(«Справочник.Приказы»); ЭлементБлокировки.УстановитьЗначение(«Ссылка», ПриказСсылка); ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый; Блокировка.Заблокировать(); // 2. Прочитать первый объект — приказ ПриказОбъект = ПриказСсылка.ПолучитьОбъект(); // 3. Прочитать второй объект – пользователя (автора приказа) АвторПриказа = ПриказОбъект.Автор.ПолучитьОбъект();

Пример установки исключительной блокировки (без открытия транзакции – в предположении, что ранее уже была открыта системная транзакция):

// 1. Установить исключительную блокировку для ответственного чтения объекта с целью его дальнейшего изменения Блокировка = Новый БлокировкаДанных; ЭлементБлокировки = Блокировка.Добавить(«Справочник.Приказы»); ЭлементБлокировки.УстановитьЗначение(«Ссылка», ПриказСсылка); ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; // Можно не указывать, т.к. по умолчанию Исключительный Блокировка.Заблокировать(); // 2. Получить объект для его дальнейшей модификации Объект = ПриказСсылка.ПолучитьОбъект(); // Выполнить блокировку объекта от изменения другими режимами или пользователями Объект.Заблокировать(); Объект.Реквизит = … // 3. Записать измененный объект Объект.Записать();

Использование чтения вне транзакции

2.1. Не следует завязывать логику механизмов на эффект грязного чтения ( возможность чтения данных незафиксированных транзакций), т.к. его нет в версионных СУБД (PostgreSQL, Oracle database), а прикладные решения должны быть работоспособны на всех СУБД, поддерживаемых платформой. Например, если фоновое задание работает в транзакции, не следует пытаться отслеживать состояние этого задания по изменениям в информационной базе – это не будет работать на версионных СУБД.

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

* Примечание: На СУБД MS SQL попытка чтения вне транзакции может завершится ошибкой «Could not continue scan with NOLOCK due to data movement». Обычно, при возникновении такой ошибки следует попытаться повторно выполнить чтение. При этом число попыток и способ обработки ошибки — отдельный предмет проектирования в каждом конкретном случае.

См. также

  • Общие сведения об избыточных блокировках
  • Обработка исключений при использовании транзакций
  • Блокирующее чтение остатков в начале транзакции