Ускоряем ЗУП 3 Корп на предприятиях с большим количеством работников

20.05.23

База данных - HighLoad оптимизация

Как только мы перешли с УПП на ЗУП 3, сразу столкнулись с огромной проблемой. Наша организация в 12 000 человек, разбив документы на 2 части по 6 000 человек не смогла за несколько часов рассчитать зарплату, и пришлось срочно заниматься оптимизацией "на ходу". В процессе выяснилось немало ключевых мест, и в результате, как правило, небольшими "инъекциями", удалось качественно (часть участков с часов до минут, а местами и до секунд) ускорить процесс и дать возможность расчетчикам выполнять работу в сжатые сроки. В результате этого процесса получилось расширение. Которое можно просто применить на типовой ЗУП Корп.  Если будет потребность для адаптации под конкретный релиз пишите, сделаем.

Скачать файлы

Наименование Файл Версия Размер
Ускоряем типовой ЗУП 3 Корп (для 3.1.25.40, 3.1.25.136)
.cfe 93,46Kb
11
.cfe 93,46Kb 11 Скачать

Анализ замеров показал следующие "классические" места для ускорения, и одно было нетривиальное.

К "классическим" можно отнести достаточно типовые варианты, которые типовая ЗУП нередко игнорирует, но которые сильно проявляют себя при увеличении объема данных (геометрическая прогрессия) и при использовании RLS.

  1. Добавление индекса в запросы
  2. Добавление индекса в таблицы значений для ускорения поиска в коде 1С
  3. "Выбрать различные". Казалось бы, неэффективный способ, но по практике есть места в запросах, которые возвращают большое количество дублей в промежуточных таблицах, и в последующих запросах возникает излишний перебор.
  4. "Предотбор" элементов в "где". Особенно эффективно это когда меняем отбор для  "разыменования"

И был найден очень "безобидный" кусок в очередном релизе ЗУП

Общий модуль УчетСреднегоЗаработка

	ИсключаемыеСтроки = Новый Массив;
	Для Каждого СтрокаНачислений Из Начисления Цикл 
		Если ИсключаемыеСтроки.Найти(СтрокаНачислений) <> Неопределено Тогда 
			Продолжить;
		КонецЕсли;
		Для Каждого ТекущаяСтрока Из Начисления Цикл
			Если СтрокаНачислений = ТекущаяСтрока Тогда
				Продолжить;
			КонецЕсли;
			Если ИсключаемыеСтроки.Найти(ТекущаяСтрока) <> Неопределено Тогда 
				Продолжить;
			КонецЕсли;
			ЗначенияКолонокСовпадают = Истина;
			Для Каждого ИмяКолонки Из ИменаКолонок Цикл 
				Если СтрокаНачислений[ИмяКолонки] <> ТекущаяСтрока[ИмяКолонки] Тогда
					ЗначенияКолонокСовпадают = Ложь;
					Прервать;
				КонецЕсли;
			КонецЦикла;
			Если Не ЗначенияКолонокСовпадают Тогда 
				Продолжить;
			КонецЕсли;
			Если СтрокаНачислений.Сумма = -ТекущаяСтрока.Сумма Тогда 
				ИсключаемыеСтроки.Добавить(СтрокаНачислений);
				ИсключаемыеСтроки.Добавить(ТекущаяСтрока);
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	ИсключаемыеНачисления = Начисления.Скопировать(ИсключаемыеСтроки);
	Для Каждого ИсключаемаяСтрока Из ИсключаемыеСтроки Цикл 
		Начисления.Удалить(ИсключаемаяСтрока);
	КонецЦикла;
	

В результате запуска профайлера выяснилось, что данный кусок кода для 6 000 сотрудников работает порядка 4 часов.

Потому что в начислений "всего" порядка 138 000 строк, и десяток колонок.

Но данный код геометрически увеличил циклы до миллиардов итераций.

Переделал этот кусок на генерацию запрос скуля.

	Запрос.УстановитьПараметр("Начисления",Начисления);
	Запрос.Текст="ВЫБРАТЬ Начисления.Сумма как Сумма,Начисления.Сторно как Сторно";
	Для Каждого ИмяКолонки Из ИменаКолонок Цикл 
			Запрос.Текст=Запрос.Текст+", 
				|Начисления."+ИмяКолонки+" как "+ИмяКолонки;
	КонецЦикла;		
	Запрос.Текст=Запрос.Текст+"
	|Поместить ВТ_Начисления 
	|из &Начисления как Начисления;
	|
	| Выбрать 
	|	Сумма(Начисления.Сумма) как Сумма";
	Для Каждого ИмяКолонки Из ИменаКолонок Цикл 
			Запрос.Текст=Запрос.Текст+",
				|Начисления."+ИмяКолонки+" как "+ИмяКолонки;
	КонецЦикла;		
	Запрос.Текст=Запрос.Текст+"
	|Поместить ВТ_ИсключаемыеСтроки 
	|	из ВТ_Начисления как Начисления
	|	Сгруппировать по 1";
	Для Каждого ИмяКолонки Из ИменаКолонок Цикл 
			Запрос.Текст=Запрос.Текст+",
				|Начисления."+ИмяКолонки;
	КонецЦикла;		
	Запрос.Текст=Запрос.Текст+"
	| Имеющие Сумма(Начисления.Сумма)=0
	|	;
	|
	|	ВЫБРАТЬ Начисления.Сумма как Сумма,Начисления.Сторно как Сторно";
	Для Каждого ИмяКолонки Из ИменаКолонок Цикл 
			Запрос.Текст=Запрос.Текст+", 
				|Начисления."+ИмяКолонки+" как "+ИмяКолонки;
	КонецЦикла;		
	Запрос.Текст=Запрос.Текст+"
	|	из ВТ_Начисления как Начисления
	|	Левое соединение
	|	 ВТ_ИсключаемыеСтроки как ВТ_ИсключаемыеСтроки
	|   по Истина ";
	Для Каждого ИмяКолонки Из ИменаКолонок Цикл 
			Запрос.Текст=Запрос.Текст+" и 
				|Начисления."+ИмяКолонки+" = ВТ_ИсключаемыеСтроки."+ИмяКолонки;
	КонецЦикла;		
	Запрос.Текст=Запрос.Текст+"
	|	где ВТ_ИсключаемыеСтроки."+ИмяКолонки+" есть NULL;
	|
	| Выбрать * из ВТ_ИсключаемыеСтроки;";
	Пакет = Запрос.ВыполнитьПакет();
	Начисления = Пакет[2].Выгрузить(); 
	ИсключаемыеНачисления = Пакет[3].Выгрузить();

В итоге вся работа происходит за считанные секунды.

Проверено на релизах ЗУП КОРП 3.1.25.40 и 3.1.25.136

 

PS.

Вполне вероятно будет работать и на ERP 2, в связи с тем что внутри ерп модули ЗУП 3 корп

ЗУП3 ускорение оптимизация

См. также

Опыт оптимизации 1С на PostgreSQL

HighLoad оптимизация Бесплатно (free)

При переводе типовой конфигурации 1C ERP/УТ/КА на PostgreSQL придется вложить ресурсы в доработку и оптимизацию запросов. Расскажем, на что обратить внимание при потерях производительности и какие инструменты/подходы помогут расследовать проблемы после перехода.

20.11.2023    5845    ivanov660    4    

63

ТОП проблем/задач у владельцев КОРП лицензий 1С на основе опыта РКЛ

HighLoad оптимизация Бесплатно (free)

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

15.11.2023    3713    a.doroshkevich    20    

65

Начните уже использовать хранилище запросов

HighLoad оптимизация Запросы

Очень немногие из тех, кто занимается поддержкой MS SQL, работают с хранилищем запросов. А ведь хранилище запросов – это очень удобный, мощный и, главное, бесплатный инструмент, позволяющий быстро найти и локализовать проблему производительности и потребления ресурсов запросами. В статье расскажем о том, как использовать хранилище запросов в MS SQL и какие плюсы и минусы у него есть.

11.10.2023    13728    skovpin_sa    14    

83

Как эффективно настроить autovacuum в Postgres для 1С

HighLoad оптимизация Администрирование СУБД Платформа 1С v8.3 Бесплатно (free)

Кто не любит убирать мусор? Думаю, практически все, а вот в Postgres это обязательный ритуал для эффективной работы. Как эффективно настроить уборку за 1С в Postgres, можно прочитать в этой статье и еще раз задуматься о бесплатности Postgres.

05.08.2023    4236    1CUnlimited    5    

48

MS SQL Server: изучаем планы запросов

Запросы HighLoad оптимизация Запросы Бесплатно (free)

Многие знают, что для ускорения работы запроса нужно «изучить план». При этом сам план обычно обескураживает: куча разноцветных иконок и стрелочек; ничего не понятно, но очень интересно! Аналитик производительности Александр Денисов на конференции Infostart Event 2021 Moscow Premiere рассказал, как выполняется план запроса и что нужно сделать, чтобы с его помощью находить проблемы производительности.

20.06.2023    11075    Филин    37    

101

Простой способ проверки быстродействия

HighLoad оптимизация Платформа 1С v8.3 Бесплатно (free)

Простой (а точнее, мегапростой) способ проверки быстродействия, когда очень важно его, быстродействие, улучшить

10.04.2023    3523    vkrivov@yandex.ru    15    

36
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. user1175436 24 10.05.23 14:45 Сейчас в теме
добрый день!

1500 сотрудников - без расширения начисление ЗП заполняется за 1 минуту , а с расширением - 1 минута 20 секунд
2. ZAOSTG 39 10.05.23 21:15 Сейчас в теме
(1)Странно очень. Профайлер есть?
3. Gesperid 2 11.05.23 11:42 Сейчас в теме
Ваш запрос делает не то, что код.
Например, в такой таблице:
ИдентификаторСтроки Сумма
84 -168
84 168
84 -190

Запрос ничего не удалит, а код удалит две первые строки.
Я бы начал с индексации таблиц.
	//+
	ИменаКолонокСтрокой = СтрСоединить(ИменаКолонок, ",");
	Начисления.Индексы.Добавить(ИменаКолонокСтрокой);

	ИсключаемыеСтрокиСоответствие = Новый Соответствие;
	//-
	
	Для Каждого СтрокаНачислений Из Начисления Цикл
		
		//+ 
		Если ИсключаемыеСтрокиСоответствие[СтрокаНачислений] <> Неопределено Тогда 
			Продолжить;
		КонецЕсли;
		
		КлючСтроки = Новый Структура(ИменаКолонокСтрокой);
		ЗаполнитьЗначенияСвойств(КлючСтроки, СтрокаНачислений);
		
		Для каждого СтрокаПотенциальная Из Начисления.НайтиСтроки(КлючСтроки) Цикл 
			Если СтрокаНачислений = СтрокаПотенциальная Тогда
				Продолжить;
			КонецЕсли;  
			
			Если ИсключаемыеСтрокиСоответствие[СтрокаПотенциальная] <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			//
			Если СтрокаНачислений.Сумма = -СтрокаПотенциальная.Сумма Тогда 
				ИсключаемыеСтрокиСоответствие.Вставить(СтрокаНачислений);
				ИсключаемыеСтрокиСоответствие.Вставить(СтрокаПотенциальная);
				Прервать;
			КонецЕсли;
		КонецЦикла;          
		
		Продолжить;
		//-

		Если ИсключаемыеСтроки.Найти(СтрокаНачислений) <> Неопределено Тогда 
			Продолжить;
		КонецЕсли;
		
		Для Каждого ТекущаяСтрока Из Начисления Цикл 
			Если СтрокаНачислений = ТекущаяСтрока Тогда
				Продолжить;
			КонецЕсли;  
			
			Если ИсключаемыеСтроки.Найти(ТекущаяСтрока) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;  
			
			//
			ЗначенияКолонокСовпадают = Истина;
			Для Каждого ИмяКолонки Из ИменаКолонок Цикл 
				Если СтрокаНачислений[ИмяКолонки] <> ТекущаяСтрока[ИмяКолонки] Тогда
					ЗначенияКолонокСовпадают = Ложь;
					Прервать;
				КонецЕсли;
			КонецЦикла;
			Если Не ЗначенияКолонокСовпадают Тогда 
				Продолжить;
			КонецЕсли;
			
			//
			Если СтрокаНачислений.Сумма = -ТекущаяСтрока.Сумма Тогда 
				ИсключаемыеСтроки.Добавить(СтрокаНачислений);
				ИсключаемыеСтроки.Добавить(ТекущаяСтрока);
				Прервать;
			КонецЕсли;
		КонецЦикла;
		
	КонецЦикла;
	
	//+
	Для каждого КлючЗначение Из ИсключаемыеСтрокиСоответствие Цикл
		ИсключаемыеСтроки.Добавить(КлючЗначение.Ключ);
	КонецЦикла;
	//-
Показать
4. ZAOSTG 39 22.05.23 12:08 Сейчас в теме
(3)Согласен, запрос может пропустить часть данных для сворачивания
Но думаю что предложенная Вами ситуация в основном искусственная - отсторнировали и снова сделали запись
Ради таких "пропусков", делающих излишние проверки далее, жертвовать часы работы "оригинал" или "переделка" мы выбрали переделка

Ваш вариант кода тестирую на наших данных. Если поможет -огромное спасибо за совет, помещу в расширение
cleaner_it; +1 Ответить
5. Kesak 12 25.05.23 00:19 Сейчас в теме
(4) Тестирование ещё в процессе?
6. ZAOSTG 39 25.05.23 05:58 Сейчас в теме
(5)Так как код - часть, и не взлетел с ходу, потому что ИсключаемыеСтроки определяются ниже, а тут уже ищутся, не успел -завал. В рабочей уже несколько месяцев используют запрос -замечаний нет
7. artbear 1444 03.06.23 12:58 Сейчас в теме
(3) Ага, хороший вариант, но не хватает поиска в таблице по сумме, ведь она заранее известна!
плюс структуру КлючСтроки не нужно создавать каждый раз.

в итоге код становится практическим линейным

мой вариант
ИменаКолонокСтрокой = СтрСоединить(ИменаКолонок, ",");
ИменаКолонокСтрокой = ИменаКолонокСтрокой + ",Сумма";

Начисления.Индексы.Добавить(ИменаКолонокСтрокой);

ИсключаемыеСтрокиСоответствие = Новый Соответствие;
КлючСтроки = Новый Структура(ИменаКолонокСтрокой);

Для Каждого СтрокаНачислений Из Начисления Цикл
	
	Если ИсключаемыеСтрокиСоответствие[СтрокаНачислений] <> Неопределено Тогда 
		Продолжить;
	КонецЕсли;
	
	ЗаполнитьЗначенияСвойств(КлючСтроки, СтрокаНачислений);
	КлючСтроки.Сумма = -СтрокаПотенциальная.Сумма;
	
	ИсключатьСтроку = Ложь;
	Для каждого СтрокаПотенциальная Из Начисления.НайтиСтроки(КлючСтроки) Цикл 
		ИсключатьСтроку = Истина;
		Если ИсключаемыеСтрокиСоответствие[СтрокаПотенциальная] = Неопределено Тогда
			ИсключаемыеСтрокиСоответствие.Вставить(СтрокаПотенциальная);
			ИсключаемыеСтроки.Добавить(СтрокаПотенциальная);
		КонецЕсли;
	КонецЦикла;    
	Если ИсключатьСтроку Тогда      
		ИсключаемыеСтрокиСоответствие.Вставить(СтрокаНачислений);
		ИсключаемыеСтроки.Добавить(СтрокаНачислений);
	КонецЕсли;
КонецЦикла;
Показать
8. ZAOSTG 39 06.06.23 15:32 Сейчас в теме
Кстати
в релиз 3.1.25.136 1С снова подложили pork насчет производительности
сейчас доделываю обработку по подготовке регистров и в расширение добавлю ускорение
регистры по отпускам и аналитикам затрат - страшно медленно -на нашей базе это более 7 часов обработка первой части
и десяток часов когда пользователям уже можно работать...
9. ILM 240 08.06.23 10:58 Сейчас в теме
Валентин, напишите как с УПП переходили?
10. ZAOSTG 39 11.06.23 20:05 Сейчас в теме
(9)Мучительно ;)
1 - различное хранение данных. В ЗУП сделали "гениальную" реализацию - в один день можно сделать несколько кадровых операций. Добавляют секунду. в УПП такого нельзя. в итоге пришлось танцевать с бубном чтобы в УПП кадровая история
2 - обмены. как с кадровыми так и ведомости. Аналитика разрозненная, трансформации,
3 -скорость обмена. у нас ведомости на 6 000 чел уходя часами -механизм кривоватый, "дубовый" -пришлось его срочно ускорять

Перешли на ЗУП потому что "голова" решила что "будет как у нас!" Но одно дело офисный планкттон с его расчетом зп, другое строительная организация...
Оставьте свое сообщение