ЦБ розницы тут ласково называют "планета Шелезяка". Потому что "населена роботами". В нее стекаются данные от кучи розничных точек (уж далеко за две тысячи), живут в ней пять десятков "роботов" (фоновых заданий) двух типов - "выгрузка" да "загрузка", кидают туда-сюда гигабайты данных. Наиболее частая проблема - кто-то нажал "не туда" (а иной раз - сознательно "именно туда"), в план обмена зарегистрировалось "полмира" - и поехало оно выгружаться, с коллапсом серверов 1С (оно не только файлы кидает, оно их еще и архивирует - экономия трафика однако).
Иду на сервера кластера 1С - тихо.. нет признаков массовой выгрузки или загрузки. Что-то другое.
Смотрю счетчики sql-сервера - ага, вот оно:
Загрузка по CPU близка к 100% - это при среднесуточном значении в 50%.
Все 32 logical processors - под завязку.
Смотрим на sql-сервере запросы, потребляющие CPU,
вот так:
select
sql = cast(SUBSTRING( ST.text, (er.statement_start_offset/2) + 1,((CASE er.statement_end_offset WHEN -1 THEN DATALENGTH(ST.text) ELSE er.statement_end_offset END - er.statement_start_offset)/2) + 1)as varchar(max)),
er.session_id, CPU = cast( er.cpu_time as bigint), lReads = er.logical_reads, lWrites = er.writes, fReads = er.reads, Duration = cast( er.total_elapsed_time as bigint), er.start_time
from
sys.dm_exec_requests AS er with(nolock)
outer APPLY sys.dm_exec_sql_text(er.sql_handle) as ST
where er.session_id > 50
order by CPU desc
и еще с помощью sp_WhoIsActive:
exec sp_WhoIsActive @sort_order = '[CPU] DESC'
Результат один и тот же:
Стандартный в студии "производительность - запросы с наибольшим временем ЦП" показал бы, вероятно, то же самое.
Захожу в "консоль серверов 1С", в соединения соответствующей БД - оказыватся, полку "шелезяк" прибыло, появился новый вид фоновых "ФормированиеЗаказа":
Эти новые фоновые CPU на sql-сервере скушали, и памяти на серверах кластера 1С нахватали от души.
Ищу документацию по "ФормированиеЗаказа", иду к соответствующему ведущему программисту.
Он и говорит : "да, вот две недели назад с понедельника потихонечку и запустили.. заказ в точку теперь с серверной стороны идет - расчет всякой красивой статистики, сезонность, мощный математический аппарат, все дела.. При тестировании все было отлично!".
Ладно, копаю дальше..
Коль работает две недели - глянем лидеров по CPU за две недели на сервере (этим инструментом)
На первые места вылезли странные вставки во времянки
INSERT INTO #ttNN (VAL) VALUES(@P1)
и, ага, то самое
INSERT INTO #ttNN (VAL,VAL) VALUES(@P1,@P2)
Количество зафиксированных в sys.dm_exec_query_stats экземпляров запросов обоих видов - изрядно более обычного. В расшифровке видно, что и количество исполнений одного экземпляра - огромное. Миллионы вставок.
Постфактум посмотрел более короткий период - полчаса максимальной загрузки сервера по CPU, с 16:10 по 16:40 - тут еще более наглядно:
Попробую поймать контексты исполнения данного запроса
Несколько (15 штук) уже попались, это
----------- вес контекста 15.00 ---------
ОбщийМодуль.Фоновые.Модуль : 2658 : Результат = Документы.ПулЗаказов.СозданиеЗаказовРозн(Параметры);
Документ.ПулЗаказов.МодульМенеджера : 939 : Док.ЗаполнитьЗаказДанными();
Документ.ЗаказТочки.МодульОбъекта : 3898 : Запрос = ПолучитьПолныйЗапрос(Открытие,ДляОтчета,Пересчет,СтруктураПараметров);
Документ.ЗаказТочки.МодульОбъекта : 3437 : ТаблНастр = Справочники.НастройкиЗаказаТочки.Определить...
Справочник.НастройкиЗаказаТочки.МодульМенеджера : 471 : ПолучитьСервисныеВременныеТаблицы(...);
Справочник.НастройкиЗаказаТочки.МодульМенеджера : 309 : Запрос.Выполнить();
для INSERT INTO #ttXX (VAL) VALUES(@P1)
На фоне сотен запусков этого как-то маловато, да и контекст для INSERT INTO #ttXX (VAL,VAL) VALUES(@P1,@P2) не определен.
Настраиваю техжурнал на серверах кластера, дополняя его вот такой конструкцией:
<log history="1" location="Z:\Server1C\logsInsTMP">
<event>
<eq property="name" value="dbmssql"/>
<like property="Sql" value="%INSERT INTO #tt%(%)%VALUES(%"/>
</event>
<property name="all"/>
</log>
Жду.
Директории Z:\Server1C\logsInsTMP за 20 минут вырастают до 2 и 1,5Gb (два сервера к кластере).
Убираю настройку, смотрю содержимое части logsInsTMP (по UID rphost-ов отобрал пяток объемистых директорий) - группировка по контексту с выводом количества событий.
Картинка вот такая:
7 729
INSERT INTO #ttNN (VAL,VAL) VALUES(?,?)
ОбщийМодуль.Фоновые.Модуль : 2658 : Результат = Документы.ПулЗаказов.СозданиеЗаказовРозн(Параметры);
Документ.ПулЗаказов.МодульМенеджера : 939 : Док.ЗаполнитьЗаказДанными();
Документ.ЗаказТочки.МодульОбъекта : 3898 : Запрос = ПолучитьПолныйЗапрос(Открытие,ДляОтчета,Пересчет,СтруктураПараметров);
Документ.ЗаказТочки.МодульОбъекта : 3437 : ТаблНастр = Справочники.НастройкиЗаказаТочки.Определить...
Справочник.НастройкиЗаказаТочки.МодульМенеджера : 470 : ПроверкаОграниченияТовары = Рез...
Справочник.НастройкиЗаказаТочки.МодульМенеджера : 269 : Возврат ПолучитьРезультатЗапрета(...);
Справочник.НастройкиЗаказаТочки.МодульМенеджера : 244 : Возврат Запрос.Выполнить().Выгрузить();
7 479
INSERT INTO #ttNN (VAL,VAL,VAL) VALUES(?,?,?)
ОбщийМодуль.Фоновые.Модуль : 2658 : Результат = Документы.ПулЗаказов.СозданиеЗаказовРозн(Параметры);
Документ.ПулЗаказов.МодульМенеджера : 939 : Док.ЗаполнитьЗаказДанными();
Документ.ЗаказТочки.МодульОбъекта : 3817 : Результат = Запрос.Выполнить();
4 839
INSERT INTO #ttNN (VAL,VAL) VALUES(?,?)
ОбщийМодуль.Фоновые.Модуль : 2658 : Результат = Документы.ПулЗаказов.СозданиеЗаказовРозн(Параметры);
Документ.ПулЗаказов.МодульМенеджера : 939 : Док.ЗаполнитьЗаказДанными();
Документ.ЗаказТочки.МодульОбъекта : 3898 : Запрос = ПолучитьПолныйЗапрос(Открытие,ДляОтчета,Пересчет,СтруктураПараметров);
Документ.ЗаказТочки.МодульОбъекта : 3634 : ТаблЗап = Справочники.РазрешенияНаЗаказТовараВТочках.Определить...
Справочник.РазрешенияНаЗаказТовараВТочках.МодульМенеджера : 315 : ПроверкаОграничения = ПолучитьРезультатЗапрета(...);
Справочник.РазрешенияНаЗаказТовараВТочках.МодульМенеджера : 239 : Возврат Запрос.Выполнить().Выгрузить();
566
INSERT INTO #ttNN (VAL) VALUES(?)
ОбщийМодуль.Фоновые.Модуль : 2658 : Результат = Документы.ПулЗаказов.СозданиеЗаказовРозн(Параметры);
Документ.ПулЗаказов.МодульМенеджера : 1037 : Док.ЗаполнитьНачальныеНастройки_Модуль(Доформирование);
Документ.ЗаказТочки.МодульОбъекта : 4482 : ТЗ = Справочники.НастройкиЗаказаТочки.ЗаполнитьНачальныеНастройкиЗак(...);
Справочник.НастройкиЗаказаТочки.МодульМенеджера : 717 : ПроверкаОграничения = ПолучитьРезультатЗапрета(...);
Справочник.НастройкиЗаказаТочки.МодульМенеджера : 244 : Возврат Запрос.Выполнить().Выгрузить();
Ну вот, ситуация примерно ясна.
Крутится она вокруг двух справочников, НастройкиЗаказаТочки и РазрешенияНаЗаказТовараВТочках.
Справочники - для удобства - содержат не сами ссылки на точку, а ее параметр. И не сами товары - а их свойства.
Например, что-то вроде "точка вышей категории" / "дней запаса для товаров Типа А" / "100".
Ну, точки гуляют меж категориями, товары меняют характеристики, а тут один раз задал - и не паришься.
Удобно.
Но перед использованием все это счастье разворачивается в плоские таблицы.
Вместо
"точка высшей категории" / "дней запаса для товаров Типа А" / "100"
будет несколько тысяч записей вида
"магазин 135" / "товар1" / "100"
"магазин 135" / "товар2" / "100"
и т. д.
Сделано это все.. сюрприз - через таблицы значений.
В ф-ю менеджера справочника кидаем списки точек и прочих параметров, они попадают в запрос при помощи конструкций Запрос.УстановитьПараметр("Таблица1",Таблица1); / первая порция Insert/, далее получаем результат, который заливаем в таблицу значений на стороне сервера 1С (Возврат Запрос.Выполнить().Выгрузить();), таблицу возвращаем в формирующий документ, далее ее - правильно - опять посылаем на сторону sql-сервера (Запрос.УстановитьПараметр("Настройки", ТаблНастр);) /вторая порция Insert/.
Таблицы немаленькие (обрабатываем за итерацию ~100 точек * 5000 товаров * 10 параметров), строк миллионы - и все это медленно и печально гуляет туда-сюда.
И, как ни странно, жрет CPU.
Переделать это все как-то кардинально "не представляется возможным по техническим причинам" ("Ну ты ж понимаешь, это не единственный косяк, и если все перебрать "как надо", то переделок тут на тучу часов, ну где их взять?").
Договорились просто - не будем выгружать в таблицы значений, будем параметром передавать объект Запрос с менеджером временных таблиц, да создавать в нем еще одну времянку.