понедельник, 21 декабря 2015 г.

Оптимизация обработки "Редактор свойств номенклатуры"

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

Итак предыстория такая: есть база с УТ 11 которая работала на выделенном сервере(SQL сервер отдельно от сервера 1С, при этом сам сервер 64 битный)  , Периодически появляются проблемы с сервером, проявляется это резким замедлением производительности в самый неожиданный момент, Если в момент тормозов зайти на сервер то можно было увидеть, что процессы 1с(rphost) могли съесть все имеющуюся на сервере память, а скорее всего даже гораздо больше. Вычислить проблему мне удалось подглядев в консоль администрирования 1С в закладке "сеансы" в колонке "Данные СУБД (5 мин)", было очень большое значение, что говорило, что c сервера поступают большой объем данных.

Поговорив с пользователем, я выяснил, что он запустил обработку "Редактор свойств номенклатуры", с большим количеством позиций, в результате чего обработка зависла и ему пришлось убивать 1С через диспетчер задач. Так же мне позже сказали что этой обработкой им советовали пользоваться очень осторожно, т.к. в ней можно получать не более 300 позиций номенклатуры. К слову сказать в текущей базе используется огромное количество свойств номенклатуры(больше 100),

После того как я выяснил что это за обработка, я решил убедиться что эта та самая обработка, которая портит работу сервера. Я решил попробовать ее запустить(на тот момент я не знал о советах по ограничению выбираемого количества номенклатур. Поэтому я решил запустить ее по для получения всего списка номенклатур. И вот что я увидел после запуска на сервере:

Как видно из скриншота, я практически сразу понял, что обработка сейчас завесит сервер, поэтому я убил свое соединение, прежде чем сервер стал тормозить.


Мне стало даже интересно, что так сильно убивает память сервера. Ограничив список небольшим количеством номенклатур, я запустил обработку еще раз,но при этом включив замер производительности и вот что увидел: 

Как видно из этого скриншота, что 77 процентов работы обработки, занимает поиск свойства в таблице которая была получена из результата запроса. Недолго думая я переписал данный кусок кода изменив его таким образом, что бы поиск по таблице не производился:

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


В результате такого изменения обработка уже позволяла вывести более 3-6 тысяч позиций за один раз. Однако такой результат меня не совсем устраивал, т.к. если пользователь забудет указать отбор, то сервер все равно зависнет, и все мои действия не дадут нужного результата.

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

ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);

Это говорило о том, что сервер пытается выгрузить с большой объем данных. Открыв Схему компоновки данных я увидел следующий запрос:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
Номенклатура.Производитель,
Номенклатура.Наименование,
Номенклатура.Код
ПОМЕСТИТЬ ВсяНоменклатура
ИЗ
Справочник.Номенклатура КАК Номенклатура

ИНДЕКСИРОВАТЬ ПО
Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Свойство КАК Свойство,
НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Свойство.Родитель КАК ГруппаСвойств
ПОМЕСТИТЬ Свойства
ИЗ
Справочник.НаборыДополнительныхРеквизитовИСведений.ДополнительныеРеквизиты КАК НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты
ГДЕ
НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Ссылка В
(ВЫБРАТЬ
НаборыДополнительныхРеквизитовИСведений.Ссылка
ИЗ
Справочник.НаборыДополнительныхРеквизитовИСведений КАК НаборыДополнительныхРеквизитовИСведений
ГДЕ
НаборыДополнительныхРеквизитовИСведений.Родитель В (ЗНАЧЕНИЕ(Справочник.НаборыДополнительныхРеквизитовИСведений.Справочник_Номенклатура)))
И НЕ НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Ссылка.ПометкаУдаления
И НЕ НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Свойство.ПометкаУдаления
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВсяНоменклатура.Номенклатура,
ВсяНоменклатура.Производитель,
ВсяНоменклатура.Наименование,
ВсяНоменклатура.Код,
Свойства.Свойство
ПОМЕСТИТЬ НоменклатураВсеСвойства
ИЗ
ВсяНоменклатура КАК ВсяНоменклатура,
Свойства КАК Свойства
ГДЕ
НЕ Свойства.Свойство ЕСТЬ NULL 
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
НоменклатураДополнительныеРеквизиты.Ссылка КАК Номенклатура,
НоменклатураДополнительныеРеквизиты.Свойство КАК Свойство,
НоменклатураДополнительныеРеквизиты.Значение КАК Значение,
НоменклатураДополнительныеРеквизиты.ТекстоваяСтрока КАК ТекстоваяСтрока
ПОМЕСТИТЬ ДополнительныеРеквизитыНоменклатуры
ИЗ
Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты

ИНДЕКСИРОВАТЬ ПО
Номенклатура,
Свойство
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
НоменклатураВсеСвойства.Номенклатура,
НоменклатураВсеСвойства.Производитель,
НоменклатураВсеСвойства.Наименование,
НоменклатураВсеСвойства.Свойство,
НоменклатураВсеСвойства.Код,
ЕСТЬNULL(ЗначенияСвойств.Значение, "") КАК Значение
ИЗ
НоменклатураВсеСвойства КАК НоменклатураВсеСвойства
ЛЕВОЕ СОЕДИНЕНИЕ ДополнительныеРеквизитыНоменклатуры КАК ЗначенияСвойств
ПО (ЗначенияСвойств.Номенклатура = НоменклатураВсеСвойства.Номенклатура)
И (ЗначенияСвойств.Свойство = НоменклатураВсеСвойства.Свойство)


Если его изучить, то можно заметить, что в третьем запросе(пакета запросов), выполняется полное соединение всей номенклатуры со всеми возможными свойствами. После этого к нему прицеплялись существующие данные, и потом уже готовая таблица выгружалась на сервер 1С. При существующем у нас количестве номенклатур(26 тыс) и свойств(более 100), результирующая таблица была настолько огромна, что начинала забивать память сервера, которая была не бесконечна.

С одной стороны, полное соединение в данном случае необходимо, т.к. может оказаться так, что если какое-то из свойств не назначено ни одной номенклатуре из списка, то оно не будет выведено на форму, и как следствие его нельзя будет изменить. С другой стороны получение такого огромного количества значений, многие из которых не заполнены, приводит к засорению памяти.

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

В результате этого я решил изменить запрос на такой:

ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
Номенклатура.Производитель,
Номенклатура.Наименование,
Номенклатура.Код,
1 КАК ТипПолей,
NULL КАК Свойство
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
НЕ Номенклатура.ЭтоГруппа

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
NULL,
NULL,
NULL,
NULL,
2,
ВложенныйЗапрос.Свойство
ИЗ
(ВЫБРАТЬ РАЗЛИЧНЫЕ
НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Свойство КАК Свойство,
НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Свойство.Родитель КАК ГруппаСвойств
ИЗ
Справочник.НаборыДополнительныхРеквизитовИСведений.ДополнительныеРеквизиты КАК НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты
ГДЕ
НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Ссылка В
(ВЫБРАТЬ
НаборыДополнительныхРеквизитовИСведений.Ссылка
ИЗ
Справочник.НаборыДополнительныхРеквизитовИСведений КАК НаборыДополнительныхРеквизитовИСведений
ГДЕ
НаборыДополнительныхРеквизитовИСведений.Родитель В (ЗНАЧЕНИЕ(Справочник.НаборыДополнительныхРеквизитовИСведений.Справочник_Номенклатура)))
И НЕ НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Ссылка.ПометкаУдаления
И НЕ НаборыДополнительныхРеквизитовИСведенийДополнительныеРеквизиты.Свойство.ПометкаУдаления) КАК ВложенныйЗапрос
Как видно из запроса, это уже не пакет запросов, а объединение двух запросов, с разделением на тип получаемых данных. в первом получается список номенклатур, которые отбирает пользователь, во втором отбираются список свойств, которые требуются пользователю.

Таким образом СКД Возвращает только данные которые необходимы пользователю. А потом в самой обработке, я получаю весь список свойств(для создания колонок на управляемой форме) и список номенклатуры для получения уже установленных значений.
В результате таких изменений, обработка стала выводить весь список номенклатур 26 тыс позиций менее чем за минуту. При этом память съедается совсем не много, что не критично, т.к. ее всегда должно быть с запасом, для таких случаев, ну и в подтверждение результат нагрузки на сервер, при получении всего списка:

Как видно результат есть и очень отличается от первоначального варианта.

Комментариев нет:

Отправить комментарий