Как лучше записывать данные в регистр сведений 1С: Менеджер записи или Набор записей?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке в среде 1С:Предприятие перед каждым программистом рано или поздно встает классическая дилемма: какой инструмент выбрать для записи данных в регистр сведений — РегистрСведенийМенеджерЗаписи или РегистрСведенийНаборЗаписей? Особенно остро этот вопрос звучит, когда регистр состоит всего из нескольких измерений (например, связь документов «один к одному»), и критически важно избежать ошибок уникальности, сохранив при этом высокую производительность системы.

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

Особенности перезаписи данных: МенеджерЗаписи vs НаборЗаписей

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

Поведение платформы напрямую зависит от параметров, которые мы передаем методу записи. Если мы используем параметр замещения со значением Истина, платформа не выдаст ошибку, а перезапишет существующие данные. Выясним причину такого поведения для обоих объектов:

Сравнение синтаксиса и объема кода

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

Вариант 1. Запись с использованием Менеджера записи

Этот вариант привлекает разработчиков лаконичностью кода. Нам не нужно явно объявлять отборы — достаточно заполнить свойства измерений и ресурсов объекта:


НоваяЗапись = РегистрыСведений.СоответствиеДокументов.СоздатьМенеджерЗаписи();
НоваяЗапись.ПоступлениеТоваров = ДокументПоступление;
НоваяЗапись.АнализКачества = ДокументАнализ;
НоваяЗапись.Актуально = Истина;
НоваяЗапись.Записать(Истина);

Вариант 2. Запись с использованием Набора записей

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


НаборЗаписей = РегистрыСведений.СоответствиеДокументов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ПоступлениеТоваров.Установить(ДокументПоступление);
НаборЗаписей.Отбор.АнализКачества.Установить(ДокументАнализ);

НоваяСтрока = НаборЗаписей.Добавить();
НоваяСтрока.ПоступлениеТоваров = ДокументПоступление;
НоваяСтрока.АнализКачества = ДокументАнализ;
НоваяСтрока.Актуально = Истина;

НаборЗаписей.Записать(Истина);

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

Что происходит «под капотом» СУБД (Механизм двойного набора)

Проанализируем ситуацию с точки зрения производительности СУБД. Мало кто знает, но объект РегистрСведенийМенеджерЗаписи не является самостоятельной физической сущностью на уровне базы данных. Это всего лишь высокоуровневая объектная надстройка над набором записей.

Когда мы вызываем метод МенеджерЗаписи.Записать(Истина), платформа 1С неявно генерирует и выполняет **два набора записей** последовательно:

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

Из-за этого механизма «двойного набора» обработчики событий ПередЗаписью и ПриЗаписи в модуле набора записей регистра сведений вызываются **дважды** за одну транзакцию записи менеджера. Если в этих обработчиках прописана тяжелая бизнес-логика (например, дополнительные проверки, запросы к другим таблицам или запись в лог), производительность системы упадет в два раза.

Производительность при массовой работе и транзакции

Рассмотрим подробнее сценарии массового изменения или добавления данных. Представим, что нам необходимо обработать и записать в регистр сведений 100 000 записей.

Если мы решим реализовать это в цикле через МенеджерЗаписи, то столкнемся со следующей проблемой: каждая итерация цикла без явно открытой транзакции будет порождать свою собственную неявную транзакцию в СУБД. Это приведет к огромному числу дисковых операций ввода-вывода (I/O) и критической загрузке транзакционного лога СУБД. База данных начнет сильно тормозить, а пользователи столкнутся с ожиданиями блокировок.

В случае применения НаборЗаписей мы можем эффективно группировать данные. Программисту доступна запись порциями (батчами) — например, по 1 000 или 5 000 строк в одном наборе:


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

Использование записи наборов без замещения (параметр Ложь) для массовой вставки — один из самых быстрых способов наполнения регистра сведений, значительно превосходящий менеджер записи по скорости выполнения.

Блокировки данных и критический риск очистки регистра

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

Если выполнить следующий код:


НаборЗаписей = РегистрыСведений.СоответствиеДокументов.СоздатьНаборЗаписей();
// Отбор не установлен!
НаборЗаписей.Записать(Истина);

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

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

Функциональные ограничения Менеджера записи

Помимо вопросов производительности и блокировок, РегистрСведенийМенеджерЗаписи имеет ряд критических архитектурных ограничений, которые делают его практически неприменимым в сложных корпоративных ERP-системах:

Резюме и рекомендации

Подведем итог нашему подробному разбору. Выбор инструмента зависит от конкретной архитектурной задачи:

  1. Используйте РегистрСведенийМенеджерЗаписи только тогда, когда вам требуется интерактивная работа в интерфейсе (например, в формах списков, когда пользователь вручную редактирует одну конкретную строку, заполняя абсолютно все измерения регистра). Это оправдано простотой и лаконичностью кода.
  2. Используйте РегистрСведенийНаборЗаписей во всех остальных случаях, особенно при программной обработке данных, массовом заполнении регистров, переносе информации и интеграции. Набор записей обеспечивает максимальную производительность, гибкий контроль транзакций и блокировок, а также поддерживает важнейшие механизмы платформы (свойства обмена и дополнительные свойства).

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

← На главную