Использование транзакций при чтении данных
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. Длинные транзакции можно сократить за счет методов оптимизации запросов (см. статьи раздела «Оптимизация запросов»).
В отдельных случаях также рекомендуется разделять длинные транзакции на более мелкие порции, в пределах которых обеспечивается приемлемая целостность данных.