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

Печать (Ctrl+P)

1. Общие рекомендации по использованию транзакций.
2. Выбор: исключительная или разделяемая блокировка.
3. Избегать длинных транзакций.

1. Общие рекомендации по использованию транзакций.ээ

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

  • Чтение данных при проведении, для последующего формирования движений;
  • Чтение данных для последующей целостной передачи в другую систему, например в программы типа «Клиент банк»;
  • Выполнение групповой обработки объектов, при реструктуризации данных в обработчиках отложенного и оперативного обновления ИБ (*)

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

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

Примечание: перед модификацией ссылочных объектов, обычно, следует устанавливать на них пессимистичные объектные блокировк
Неправильно:

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

Правильно:

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

  Выборка = Запрос.Выполнить().Выбрать();

  КоличествоЗаметок = 0;
  Если Выборка.Следующий() Тогда
    КоличествоЗаметок = Выборка.КоличествоЗаметок;
  КонецЕсли;
 // 4. Записать в регистр сведений
  НаборЗаписей = РегистрыСведений.ЗаметкиПоПредмету.СоздатьНаборЗаписей();
  НаборЗаписей.Отбор.Предмет.Установить(ПредметЗаметок);
  НоваяЗапись = НаборЗаписей.Добавить();
  НоваяЗапись.Предмет = ПредметЗаметок;
  НоваяЗапись.КоличествоЗаметок = КоличествоЗаметок + 1;
  НаборЗаписей.Записать();
  ЗафиксироватьТранзакцию();
Исключение
  // 5. Если при установке блокировки возникла исключительная ситуация из-за того, что регистр уже заблокирован в другом сеансе (или по другим причинам),
  // отменить транзакцию и записать сведения об ошибке в журнал регистрации.
  ОтменитьТранзакцию();
  ЗаписьЖурналаРегистрации(НСтр("ru = 'Заметки'"), УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
  ВызватьИсключение;
КонецПопытки;

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

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

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

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

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

2. Выбор: исключительная или разделяемая блокировка

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

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

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

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

3. Избегать длинных транзакций.

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

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

3.2. Длинные транзакции можно сократить за счет методов оптимизации запросов (см. статьи раздела «Оптимизация запросов»). 
В отдельных случаях также рекомендуется разделять длинные транзакции на более мелкие порции, в пределах которых обеспечивается приемлемая целостность данных.

Previous Article
Next Article

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

Ваш адрес email не будет опубликован.

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.