Как быстро изменить отдельные строки в большом регистре сведений 1С

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

В процессе разработки и оптимизации систем на платформе 1С:Предприятие 8 мы часто сталкиваемся с задачами обновления данных в крупных регистрах сведений. Типичный сценарий: существует регистр сведений, связанный с документами, где на один документ может приходиться 50 000 и более строк. В определенный момент возникает необходимость обновить значения ресурсов или реквизитов лишь для небольшой части этих строк — например, для 10, 100 или 1000 записей.

Стандартный подход с использованием объекта НаборЗаписей при перезаписи малого количества строк из большого объема показывает крайне низкую производительность. В этой статье мы подробно разберем, почему классические методы работают медленно, как решить эту проблему с помощью МенеджераЗаписи в транзакции, а также рассмотрим революционные механизмы точечного обновления данных, появившиеся в современных версиях платформы 1С.

В чем сложность работы с Набором Записей?

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

  1. Полностью удаляет все существующие записи по данному отбору (команда DELETE).
  2. Записывает измененную коллекцию заново (команды INSERT).

Если нам нужно изменить всего 100 строк из 50 000, то при использовании набора записей нам придется сначала прочитать все 50 000 строк в память сервера приложений, изменить нужные 100 строк в цикле, а затем перезаписать весь объем обратно в базу данных. В итоге СУБД удалит 50 000 записей и вставит новые 50 000 записей. Ресурсы сервера на обработку 49 900 не изменившихся строк будут потрачены впустую, что приведет к колоссальным задержкам и блокировкам таблиц.

Попытка записывать данные построчно через НаборЗаписей с фильтрацией по уникальному идентификатору строки в цикле также не приносит успеха: на каждую строку платформа будет инициировать отдельные транзакции СУБД, что порождает огромные накладные расходы на сетевое взаимодействие между сервером 1С и сервером СУБД.

Решение 1: Использование Менеджера Записи внутри явной транзакции

Как показывает практика и реальные тесты разработчиков, для точечной модификации небольшого количества строк (до нескольких тысяч) гораздо эффективнее использовать объект МенеджерЗаписи. Вопреки старому мифу о том, что менеджер записи медленнее набора, при точечной работе он выигрывает в десятки раз. Однако здесь есть критически важное условие — вся выборка должна обрабатываться внутри единой транзакции.

Разберем по шагам правильный алгоритм реализации этого метода:


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

Почему этот код работает быстрее? Во-первых, мы избавляемся от необходимости пересылать 50 000 строк по сети между СУБД и сервером 1С. Во-вторых, открытая нами явная транзакция НачатьТранзакцию() объединяет все дисковые операции записи (запись в файлы базы данных и логи транзакций) в один общий пул. Если убрать транзакцию, СУБД будет вынуждена физически фиксировать изменения на диске для каждой отдельной строки, что замедлит работу алгоритма в разы.

Решение 2: Адаптивный (гибридный) алгоритм выбора метода записи

В реальных задачах количество изменяемых строк может динамически варьироваться. Если нам нужно изменить 10 строк из 50 000 — однозначно лидирует МенеджерЗаписи. Но если нужно обновить все 50 000 строк, то поштучный обход через менеджер записи будет выполняться бесконечно долго, и здесь гораздо выгоднее использовать один общий НаборЗаписей.

Мы рекомендуем спроектировать адаптивный алгоритм, который сам принимает решение о способе модификации данных в зависимости от объема изменений. Рассмотрим пример такого кода:


// Определяем пороговое значение (подбирается экспериментально, обычно от 500 до 2000 строк)
ПороговоеЗначение = 1000;

КоличествоИзменяемыхСтрок = МассивСтрокДляМодификации.Количество();

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

Революция в платформе 1С: новые режимы записи (Платформа 8.3.25 и выше)

В современных версиях платформы «1С:Предприятие 8» (начиная с 8.3.25) разработчики получили долгожданный нативный инструмент для решения этой проблемы. Теперь метод Записать() у объекта НаборЗаписей умеет принимать специальное системное перечисление РежимЗамещения вместо простого булева значения.

Появились новые режимы, которые кардинально меняют логику работы с СУБД:

Благодаря этому нововведению нам больше вообще не нужно предварительно читать все 50 000 записей регистра. Мы можем сформировать набор, содержащий исключительно 100 измененных строк, и выполнить быстрое обновление в СУБД одной командой:


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

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

// Выполняем точечный UPDATE в СУБД только для переданных строк
НаборЗаписей.Записать(РежимЗамещения.Обновление);

Этот метод объединяет в себе лучшие стороны: простоту групповой работы через набор и колоссальную производительность точечных запросов к СУБД.

Дополнительные рекомендации по ускорению массовой записи

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

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