Распараллеливание выполнения фонового задания
База данных - HighLoad оптимизация
Задача
Фоновое задание обрабатывает регистр сведений с накопленными данными, измерение договор, по результатам обработки что-то делается, записи из регистра удаляются. В тестовой базе отключены регламентные задания и накопилось больше 40 тысяч записей в регистре, при этом понадобилось чтобы записи отработали, но выяснилось, что в 1 поток записи будут обрабатываться часов 16, что не подходит.
Решение
Выполнять обработку в несколько потоков, в итоге записи были обработаны в течение 1 часа. Была написана универсальная процедура, чтобы ее можно было использовать в аналогичных фоновых заданиях.

Код
Универсальная процедура
В нее передаются
- таблица значений с данными,
- название колонки таблицы, по которой добавляются элементы в порцию
- имя вызвавшей процедуры для правильно нейминга создаваемых фоновых заданий
- имя процедуры, которая принмает порцию данных
Процедура ВыполнитьПроцедуруПорционно(ТаблицаДанных, ИмяВызвавшейПроцедуры, ИмяПроцедурыОбработкиПорции, ИмяКолонки, ЧислоПотоков = 0) Экспорт
ЧислоСтрокВТаблице = ТаблицаДанных.Количество();
Если ЧислоСтрокВТаблице = 0 Тогда
Возврат;
КонецЕсли;
Если ЧислоПотоков = 0 Тогда
МаксимальноеЧислоПотоков = 16;
ЧислоПотоков = Макс(Мин(Цел(ЧислоСтрокВТаблице / 500), МаксимальноеЧислоПотоков), 1);
КонецЕсли;
Если ЧислоПотоков = 1 Тогда
ПорцияДанных = ТаблицаДанных.ВыгрузитьКолонку(ИмяКолонки);
Выполнить(ИмяПроцедурыОбработкиПорции + "(ПорцияДанных)");
Возврат;
КонецЕсли;
РазмерПорции = Цел(ЧислоСтрокВТаблице/ЧислоПотоков);
МассивЗаданий = Новый Массив;
ПорцияДанных = Новый Массив;
Для НомерПотока = 1 По ЧислоПотоков Цикл
ИндексНачала = (НомерПотока - 1)*РазмерПорции;
ИндексКонца = ИндексНачала + РазмерПорции - 1;
Если (НомерПотока = ЧислоПотоков) Тогда
ИндексКонца = ЧислоСтрокВТаблице - 1;
КонецЕсли;
ПорцияДанных.Очистить();
Для Сч = ИндексНачала По ИндексКонца Цикл
ПорцияДанных.Добавить(ТаблицаДанных[Сч][ИмяКолонки]);
КонецЦикла;
НаборПараметров = Новый Массив;
НаборПараметров.Добавить(ПорцияДанных);
Задание = ФоновыеЗадания.Выполнить(ИмяПроцедурыОбработкиПорции, НаборПараметров,
ИмяВызвавшейПроцедуры + НомерПотока, СтрШаблон("%1 (порция %2)", ИмяВызвавшейПроцедуры, НомерПотока));
МассивЗаданий.Добавить(Задание);
КонецЦикла;
Если МассивЗаданий.Количество() > 0 Тогда
Попытка
ФоновыеЗадания.ОжидатьЗавершения(МассивЗаданий);
Исключение
мОшибка = ОписаниеОшибки();
ЗаписьЖурналаРегистрации(ИмяВызвавшейПроцедуры + ".Ошибка", УровеньЖурналаРегистрации.Ошибка,,,мОшибка);
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Фоновое задание, которое теперь работает параллельно
Процедура ОбновлениеСтатусов() Экспорт
ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(Метаданные.РегламентныеЗадания.СтатусовДоговоров);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Регистр.Договор КАК Договор
|ИЗ
| РегистрСведений.ЗаданияПоОбновлениюСтатусовДоговоров КАК Регистр
|ГДЕ
| НЕ Регистр.Ошибка
|
|СГРУППИРОВАТЬ ПО
| Регистр.Договор";
ТаблицаДоговоров = Запрос.Выполнить().Выгрузить();
ВыполнитьПроцедуруПорционно(ТаблицаДоговоров, "ОбновлениеСтатусов", "СтатусыДоговоровСервер.ОбновитьСтатусыДоговоров", "Договор");
КонецПроцедуры
Процедура, обрабатывающая порцию данных
Процедура ОбновитьСтатусыДоговоров(МассивДоговоров) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Регистр.Договор КАК Договор,
| Регистр.Статус КАК Статус
|ИЗ
| РегистрСведений.ЗаданияПоОбновлениюСтатусовДоговоров КАК Регистр
|ГДЕ
| НЕ Регистр.Ошибка
| И Регистр.Договор В(&МассивДоговоров)";
Запрос.УстановитьПараметр("МассивДоговоров", МассивДоговоров);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НачатьТранзакцию();
Попытка
ОбновитьСтатусДоговора(Выборка.Договор, Выборка.Статус);
МенеджерЗаписи = РегистрыСведений.ЗаданияПоОбновлениюСтатусовДоговоров.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Договор = Выборка.Договор;
МенеджерЗаписи.Статус = Выборка.Статус;
МенеджерЗаписи.Удалить();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
мОшибка = ОписаниеОшибки();
МенеджерЗаписи = РегистрыСведений.ЗаданияПоОбновлениюСтатусовДоговоров.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Договор = Выборка.Договор;
МенеджерЗаписи.Статус = Выборка.Статус;
МенеджерЗаписи.Ошибка = Истина;
МенеджерЗаписи.ТекстОшибки = мОшибка;
МенеджерЗаписи.Записать();
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Было сделано на основе статьи Как ускорить 1С – Многопоточная обработка данных
Вступайте в нашу телеграмм-группу Инфостарт
Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.