Довідковий матеріал

Використання транзакцій під час читання даних

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

Використання читання поза транзакцією

Див. також

 

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". Зазвичай у разі виникнення такої помилки слід спробувати повторно виконати читання. При цьому кількість спроб і спосіб обробки помилки — окремий предмет проєктування в кожному конкретному випадку.

Див. також

  • Загальні відомості про надлишкові блокування
  • Обробка винятків під час використання транзакцій
  • Блокувальне читання залишків на початку транзакції
Записатися телефоном