Как эффективно удалять записи из большого регистра сведений в 1С?

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

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

Разберем основные архитектурные ограничения платформы, влияние операций на СУБД и оптимальные паттерны пакетного удаления данных.

Ограничения платформы 1С и особенности структуры индексов

Проанализируем базовый механизм работы объекта РегистрСведенийНаборЗаписей. В платформе 1С нельзя установить отбор набора записей по произвольному неиндексированному измерению или реквизиту. Коллекция Отбор набора записей независимого регистра позволяет накладывать фильтры исключительно на те измерения, которые:

  1. Образуют непрерывный префикс основного кластерного индекса (начиная с первого измерения в дереве метаданных).
  2. Имеют установленное свойство «Индексировать» или признак «Ведущее».

Если попытаться обойти платформенный механизм через прямой запрос на уровне СУБД с условием по неиндексированному полю, СУБД выполнит полное сканирование таблицы (Clustered Index Scan / Full Scan). В момент удаления это заблокирует огромные диапазоны строк или приведет к эскалации блокировки до уровня всей таблицы.

Риски массового удаления: эскалация блокировок и журнал транзакций

Выясним причину, почему удаление даже нескольких тысяч строк единым набором может парализовать базу данных. В СУБД (например, MS SQL Server) действует механизм эскалации блокировок (Lock Escalation). При модификации более 5 000 записей (или удержании более 5 000 блокировок) в рамках одной транзакции СУБД автоматически заменяет точечные блокировки строк на блокировку всей физической таблицы.

В результате все параллельные фоновые задания, пытающиеся выполнить запись в этот регистр, будут ожидать снятия блокировки таблицы вплоть до превышения таймаута (Lock Timeout).

Рекомендуемый паттерн: порционное удаление через транзакции

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

Разберем алгоритм по шагам:

  1. Запросом выбираем порцию записей (например, 1 000–3 000 строк) по условиям очистки. В выборку обязательно включаем все ключевые поля (измерения), входящие в кластерный индекс.
  2. В цикле обходим выборку и удаляем записи точечно через РегистрСведенийНаборЗаписей или РегистрСведенийМенеджерЗаписи с полным отбором по ключевым полям.
  3. Группируем удаление в явные транзакции фиксированного размера (по 500–1 000 записей).
  4. Повторяем процесс в цикле до тех пор, пока выборка не вернет пустой результат.

Посмотрим на пример реализации пакетного удаления:


РазмерПакета = 1000;

Запрос = Новый Запрос;
Запрос.Текст = 
	"ВЫБРАТЬ ПЕРВЫЕ " + Формат(РазмерПакета, "ЧГ=0") + "
	|	Регистр.Организация КАК Организация,
	|	Регистр.Номенклатура КАК Номенклатура,
	|	Регистр.ПериодДействия КАК ПериодДействия
	|ИЗ
	|	РегистрСведений.КурсыВалют КАК Регистр
	|ГДЕ
	|	Регистр.ПериодДействия < &ГраницаОчистки";

Запрос.УстановитьПараметр("ГраницаОчистки", ГраницаУдаления);

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

Оптимизация структуры регистра на этапе проектирования

Рассмотрим, как правильно настроить метаданные, если регистр заведомо проектируется под регулярную очистку по определенному измерению:

  1. Порядок измерений в дереве конфигурации: Кластерный индекс формируется СУБД строго в порядке следования измерений в конфигураторе. Если поместить поле, по которому чаще всего выполняется поиск или удаление, на самую первую позицию, поиск по нему будет происходить по основному кластерному индексу без создания избыточных вторичных индексов.
  2. Свойство «Индексировать»: Включение индексирования на измерении создает отдельный некластерный индекс. Это ускоряет фильтрацию, однако существенно замедляет вставку новых данных (INSERT) и увеличивает объем файла базы данных. На регистрах с миллионными суточными оборотами не стоит включать индексирование на всех полях без явной необходимости.

Полная очистка регистра

Если стоит задача не выборочного удаления, а полной очистки таблицы, не следует удалять данные построчно. В платформе 1С для этого предусмотрен метод менеджера регистра Очистить():

РегистрыСведений.КурсыВалют.Очистить();

На уровне СУБД данный вызов транслируется в быструю операцию TRUNCATE TABLE, которая мгновенно освобождает страницы данных без раздувания журнала транзакций и без блокировок отдельных строк.

← На главную