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

Печать (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 для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.