Мы ускорили запрос в 390 раз, не переписав его логику: он не был медленным — он стоял

14.08.26

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

Мы ускорили запрос регламентной обработки в 390 раз — с 39 минут до 6 секунд, — не изменив его логику и получив построчно идентичный результат. Разгадка в том, что запрос не был медленным: из 2 353 секунд elapsed чистой работы (CPU) было лишь 124 — остальные 95 % он ждал блокировки, потому что срез последних без отбора читал весь регистр цен следом за массовой записью. Детектив по шагам с цифрами из статистики СУБД, чек-лист «проверь свою обработку за 15 минут» и один твист в финале.

В регламентной обработке ценообразования для маркетплейсов был шаг «Акции», который занимал около 50 минут. Внутри него жил один запрос, выполнявшийся 39 минут. Не «медленный запрос, который хочется ускорить» — а запрос, из-за которого регламент не влезал ни в одно окно.

Эта история — про то, как он превратился в 6 секунд. Спойлер дважды: во-первых, запрос эти 39 минут почти не работал — он стоял. Во-вторых, лечился он не там, где болело. Разбор по шагам, с цифрами из монитора СУБД — потому что именно они, а не интуиция, привели к разгадке.

 

Контекст: право на ошибку — ноль

Обработка живая: считает цены десятков тысяч SKU и публикует их в реальные кабинеты маркетплейсов работающего ритейлера. Ошибка оптимизации — это неверные цены на витрине. Поэтому весь проект шёл по жёсткому контуру: замерный стенд на копии боевой базы, одна правка — один прогон — сравнение результата с эталоном побайтово (полная таблица расчёта, ~100 колонок на позицию). Правка принималась только при идентичном результате.

Держите этот контур в голове: всё, что описано ниже, было бы азартной игрой без него.

 

Акт первый: подозреваемый очевиден. И невиновен

Запрос «удаление позиций по высокой цене» в блоке акций. Текст — обычный для таких обработок: срез последних по регистру цен, соединение с таблицей товаров акции, отбор по условию цены. Первая интуиция любого оптимизатора: «тяжёлый срез, много данных, перепишем повально на временные таблицы».

Прежде чем переписывать, мы посмотрели на факты. Штатная статистика СУБД по этому запросу — мы её только читаем, ничего не меняя. Удобнее всего она смотрится в мониторе активности SQL Server (SSMS → правый клик по серверу → «Монитор активности» → раздел «Последние ресурсоёмкие запросы»): длительность и процессорное время стоят там в соседних колонках. Консоль кластера 1С такой пары не даёт — она показывает сеансы и их «Время СУБД», и это лишь первый сигнал, куда смотреть. Доля ожиданий в таблице ниже — не отдельный счётчик, а деление: 1 − CPU/elapsed.

 

Показатель Значение
Полное время выполнения (elapsed) 2 353 с (~39 минут)
Процессорное время (CPU) 124 с (~2 минуты)
Доля ожиданий 95 % времени

 




Вчитайтесь: из 39 минут запрос работал две. Остальные 37 он ждал. Это переворачивает задачу: оптимизировать текст запроса, который занят работой 5 % времени, — значит оптимизировать 5 % проблемы. Сначала надо понять, чего он ждёт.

💡 Правило, которое дешевле выучить на чужом проекте: elapsed ≠ CPU. Пока вы не разложили время запроса на «работал» и «ждал», вы не знаете, что оптимизируете. Разница между этими числами — первое, на что стоит смотреть в любом «медленном запросе».

 

Акт второй: чего он ждал

Ответ нашёлся в расписании регламента. Запрос акций стартовал сразу после массовой записи цен — этапа, который в тот момент шёл 17 минут и активно писал в регистр цен тысячи записей, порциями, с проведением документов.

А теперь — что делал наш запрос. Его срез последних был написан так:

// срез по ВСЕМУ регистру цен: отбор только по типу цен, и тот — по строковому коду
РегистрСведений.Цены.СрезПоследних(, ТипЦен.Код = "000000164")

Срез виртуальной таблицы без отбора по номенклатуре означает: СУБД строит срез по всей таблице регистра — десятки миллионов записей, — чтобы потом соединением оставить несколько тысяч товаров акции. То есть запрос читает всё. А «читать всё» в момент, когда сосед «пишет много», — это очередь за блокировками по всей ширине таблицы. Отсюда 95 % ожиданий: широкое чтение застряло за массовой записью.

Заметьте, как связались две вещи, которые обычно разбирают порознь: «неоптимальный запрос» и «блокировки». Широкое чтение — это не только медленно, это ещё и конфликтно. Запрос, читающий лишние миллионы строк, конкурирует за них со всеми, кто в это время пишет. Сузив чтение, вы чините обе беды сразу.

 

Акт третий: лечение — одна правка

Раз запросу нужны только товары акции — отбор по ним должен стоять внутри параметров среза, а не снаружи соединением. Заодно строковый код типа цен заменяется ссылкой-параметром (условие .Код = "строка" — это скрытое соединение со справочником и фильтр по тексту без индекса):

// ПОСЛЕ: срез только по нужным товарам и типу цен по ссылке
РегистрСведений.Цены.СрезПоследних(
    ,
    ТипЦен = &ТипЦенКабинета
        И Номенклатура В (ВЫБРАТЬ Т.Номенклатура ИЗ ВТ_ТоварыАкции КАК Т))

Результат на боевом прогоне:

  До После
Запрос «удаление по высокой цене» 2 353 с 6 с
Шаг «Акции» целиком (оценка) ~50 мин ~10–12 мин
Результат запроса идентичен — сверен построчно

 




×390.
Не от гениальной правки — от того, что чинили причину, а не симптом: запрос перестал читать (и удерживать) миллионы чужих строк, и очередь за блокировками исчезла вместе с объёмом.

 

Акт четвёртый: рецидивы

Один найденный анти-паттерн — почти всегда серия. Мы прошли блок акций целиком: тот же «срез всего регистра без отбора» нашёлся ещё в пяти запросах соседних шагов — удаление недоступных к продаже, пересчёт цен после исключения, подготовка списков. Все пять исправлены той же схемой, каждый — со сверкой результата.

И это вторая привычка, которая окупается: нашли паттерн — ищите его везде. Погрепать тексты запросов обработки по «СрезПоследних(,» и по «.Код = "» — минуты; каждая находка — потенциальные минуты регламента.

 

Частые возражения — и почему мы сделали иначе

Разбор на ретро вызвал ровно те вопросы, которые зададут и в комментариях. Отвечаю заранее.

«Почему просто не перенести шаг акций подальше от записи цен?» Это лечит симптом у одного пациента. Перенос шага убрал бы очередь в этом расписании — но запрос остался бы широким: он всё так же читал бы десятки миллионов строк, всё так же был бы уязвим к любому соседу-писателю (а на общем сервере они есть всегда) и всё так же грел бы кэш СУБД чужими страницами. Сужение чтения лечит болезнь, перенос — прячет её. Кстати, после правки вопрос переноса отпал сам: 6-секундному запросу всё равно, кто рядом пишет.

«Может, дело в отсутствии индекса?» Первая версия этой гипотезы у нас тоже была. Но посмотрите ещё раз на соотношение: CPU 124 с, ожидания — 95 %. Плохой план исполнения выглядит иначе: высокий CPU, высокие чтения, малые ожидания. Индекс ускоряет работу — а наш запрос не работал, он ждал. Это и есть главная ценность разложения времени: оно говорит, какого класса у вас проблема, до того как вы начали чинить не ту.

«Срез последних по большому регистру — это же нормально, все так пишут». Все так пишут, пока регистр маленький. Виртуальная таблица без отбора масштабируется вместе с таблицей: на миллионе записей это незаметно, на десятках миллионов — 39 минут. Особая ловушка — регистры, у которых отключена таблица итогов среза последних: там СУБД строит срез «на лету» по всей истории при каждом обращении. Проверьте флаг у своих больших регистров сведений — результат может удивить.

«А почему не читать регистр напрямую запросом с МАКСИМУМ(Период)?» Потому что виртуальная таблица с отбором внутри параметров делает ровно это, но силами платформы и с правильными планами. Ручная эмуляция среза — код, который придётся сопровождать, и место для новых ошибок. Приём проще: не отказываться от виртуальных таблиц, а кормить их отборами.

 

Как проверить свою обработку за 15 минут

Рецепт без установки чего-либо, только чтение:

  1. Возьмите фактические тайминги этапов из журнала регистрации или лога самой обработки (у большинства регламентных обработок он есть). Найдите самый жирный этап.
  2. Разложите его время на CPU и ожидания по статистике сервера СУБД (консоль администратора, представления статистики запросов — доступны на чтение любому DBA). Ищите запросы, где elapsed кратно больше CPU.
  3. Погрепайте тексты запросов обработки по трём паттернам: СрезПоследних(, (срез без отбора), .Код = " (строковые сравнения), СрезПервых(,. Каждое совпадение — кандидат.
  4. Сопоставьте расписание: какие шаги-писатели идут перед вашим тяжёлым читателем или параллельно ему. Широкое чтение после массовой записи — та самая конфигурация, что дала нам 39 минут.
  5. Прежде чем править — снимите эталон результата. Хотя бы выгрузку итоговой таблицы с сортировкой и контрольной суммой. Правка без эталона — это надежда, а не инженерия.

По нашему опыту, три паттерна из пункта 3 покрывают большую часть «необъяснимо медленных» регламентов на выросших базах. Не потому, что разработчики плохие — потому, что код писался, когда таблицы были в сто раз меньше, и тогда он был честно быстрым.

И бонус-наблюдение с того же проекта про устойчивость: оптимизированные версии не просто быстрее — они стабильнее под чужой нагрузкой. Старый вариант ключевого запроса на тихом сервере шёл 358 секунд, а в час пик — 1 179; новый — 130 и 256 соответственно. Широкие запросы деградируют под нагрузкой нелинейно, потому что конкурируют за всё; узкие — почти не замечают соседей. Если ваш регламент «то влезает в окно, то нет» — это, возможно, не мистика, а мера широты ваших чтений.

 

Цена дисциплины — и что она поймала

Два слова о контуре сверки, без которого эту правку нельзя было бы выпустить в прод за разумное время. На каждую правку: фиксированный вход (снапшот внешних данных, границы дат — расчёт детерминирован), полный эталон результата, побайтовое сравнение. Звучит дорого, на деле — один раз настроить, дальше это кнопка.

За проект контур поймал две вещи, которые не поймал бы глаз: опечатку в тексте запроса (зарезервированное слово), ломавшую расчёт ровно в одном из четырёх ценовых контуров — три остальных прогона были бы зелёными; и подтвердил, что застарелый чужой баг ведёт себя одинаково до и после наших правок — то есть мы его не разбудили. Обе находки — из разряда «узнали бы от пользователей через неделю», и обе стоили бы дороже всего проекта оптимизации.

 

Что из этого унести себе

  1. Сначала elapsed vs CPU, потом всё остальное. Если разница в разы — вы ищете не «медленный запрос», а то, чего он ждёт. Это меняет и метод, и место починки.
  2. Срез последних без отбора — конфликтная мина. Он не просто читает лишнее — он воюет за это лишнее со всеми пишущими. Отбор по номенклатуре (и любым измерениям, по которым он у вас есть) — внутрь параметров виртуальной таблицы.
  3. Строковые коды в запросах — скрытые соединения. НайтиПоКоду() один раз в коде, дальше — ссылка-параметр.
  4. Смотрите на соседей по расписанию. Запрос может быть «медленным» только потому, что стартует в тень самого пишущего этапа. Иногда лечится переносом шага, у нас — сужением чтения.
  5. Ни одной правки без сверки результата. Наш контур — эталон и побайтовое сравнение после каждой правки — поймал за проект две ошибки, невидимые глазами. «Стало быстрее» без «стало так же» — это не оптимизация, а лотерея.

Весь проект, из которого взят этот эпизод, дал ускорение процессов ценообразования в 2–6 раз без изменения единой итоговой цены — но это уже другая история, с другими паттернами (запрос в цикле 810 → 43 с, поиск O(N²) 113 → 21 с, десять соединений одного регистра). Если интересно — расскажу в следующей статье.

И последний твист, который часть читателей, подозреваю, угадала по почерку: черновую работу в этом проекте — чтение кода, разбор статистики СУБД, подготовку правок и прогоны замеров — выполнял ИИ-агент (Claude Code) под управлением инженера. Все решения о выпуске принимал человек, и именно поэтому контур побайтовой сверки был обязательным: он превращает скорость агента из риска в преимущество. Как мы вообще пришли к работе агентов с продуктивом — я разбирал в предыдущих статьях про бенчмарк нейросетей на боевой базе. А спорить и делиться своими «39 минутами» приглашаю в комментарии: самые дикие elapsed/CPU-соотношения, которые вы встречали в бою, — отдельная коллекционная ценность.

 

Другие наши инструменты диагностики 1С:

Вступайте в нашу телеграмм-группу Инфостарт

оптимизация запросов срез последних блокировки elapsed CPU виртуальные таблицы производительность регистр сведений ценообразование маркетплейсы ожидания статистика СУБД запрос в цикле побайтовая сверка регламентные задания HighLoad DMV Claude Code ИИ-агент оптимизация 1С

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Инструментарий разработчика Роли и права Запросы СКД Программист Руководитель проекта 1С:Предприятие 8 Платные (руб)

Инструменты для разработчиков 1С 8.3: Infostart Toolkit. Автоматизация и ускорение разработки на управляемых формах. Легкость работы с 1С.

16500 руб.

02.09.2020    273695    1523    422    

1185

Инструментарий разработчика Запросы Программист 1С:Предприятие 8 1С:Зарплата и кадры государственного учреждения 3 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

QueryConsole1C — расширение, включающее консоль запросов с поддержкой исполняемых представлений — аналогов виртуальных таблиц, основанных на методах программного интерфейса ЗУП. Оно позволяет выполнять запросы с учётом встроенной бизнес-логики, отлаживать алгоритмы получения данных и автоматически генерировать код на встроенном языке 1С.

1 стартмани

16.05.2025    12615    158    zup_dev    32    

86

Запросы Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Столкнулся с интересной ситуацией, которую хотел бы разобрать, ввиду её неочевидности. Речь пойдёт про использование функции запроса АВТОНОМЕРЗАПИСИ() и проблемы, которые могут возникнуть.

11.10.2024    22722    XilDen    39    

113

HighLoad оптимизация Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Встал вопрос: как быстро удалить строки из ТЗ? Рассмотрел пять вариантов реализации этой задачи. Сравнил их друг с другом на разных объёмах данных с разным процентом удаляемых строк. Также сравнил с выгрузкой с отбором по структуре.

09.01.2024    46623    doom2good    50    

80

HighLoad оптимизация Системный администратор Программист Бесплатно (free)

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

20.11.2023    24733    ivanov660    7    

84

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

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

15.11.2023    14220    a.doroshkevich    23    

78

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

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

11.10.2023    28283    skovpin_sa    15    

107
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. paulwist 04.08.26 16:34 Сейчас в теме
2. gzharkoj 596 04.08.26 16:44 Сейчас в теме
по дефолту 1с ждет блокировку 20 сек, у вас больше и таймаута нет, как-то подозрительно. Понимать бы какая версия 1с, субд, режим совместимости.
3. nedomolkov.ivan 152 04.08.26 17:14 Сейчас в теме
(1) Изоляция — READ COMMITTED, RCSI выключен. В этом и вся механика: при включённой снапшот-изоляции читатель за писателем не встал бы вовсе, и истории бы не было.

(2) Про 20 секунд вы правы, и формулировка в статье это смазывает. 20 секунд — потолок ОДНОГО ожидания, а не суммарного времени оператора. Здесь один SELECT сканирует срез по всему регистру и по ходу скана многократно встаёт на короткие ожидания на данных, которые прямо сейчас переписывает соседний этап. Ни одно ожидание потолка не достигло — поэтому в журнале нет ни одной ошибки блокировки, а elapsed вырос до 2 353 при CPU 124. Таймаут ловит одно долгое ожидание, а не длинную сумму коротких.

Граница честности: разбивки по wait_type в статье нет, 95 % — это (elapsed − CPU). Атрибуция к блокировкам держится на двух косвенных фактах: совпадении с этапом массовой записи по расписанию и на том, что сужение чтения убрало разрыв целиком (6 с вместо 2 353 при построчно идентичном результате). Полный wait-stats был бы аргументом сильнее, согласен.

Версии: платформа 8.3, СУБД MS SQL (статистику брали из DMV). Точный релиз и режим совместимости наизусть не назову — база заказчика, а врать в таком разборе хуже, чем промолчать.
4. gzharkoj 596 04.08.26 18:02 Сейчас в теме
(3)
Изоляция — READ COMMITTED, RCSI выключен
- ну вот это все объясняет, это какая-то конфа на автоматических блокировках и в режиме совместимости с 8.2. Там и не такое встретишь.
8. paulwist 05.08.26 09:13 Сейчас в теме
(3)
Изоляция — READ COMMITTED, RCSI выключен


Подозрения оправдались :)

То что вы нашли косяк - это отлично.

НО возникает второй вопрос, почему долго (37 мин) сервер удерживает X-блокировки :) :) :)
5. nedomolkov.ivan 152 04.08.26 18:12 Сейчас в теме
(4) Тут как раз наоборот. READ COMMITTED без RCSI — это признак управляемых блокировок, а не автоматических: в автоматическом режиме платформа поднимает изоляцию до REPEATABLE READ / SERIALIZABLE, и картина была бы другой — конфликтовали бы диапазоны, а в журнале лежали бы таймауты. Их там нет ни одного.

RCSI выключен не по замыслу, а потому что это состояние базы по умолчанию: его отдельно никто не включал.

В этом и смысл разбора: экзотики в конфигурации нет, связка «управляемые + RCSI off» стоит у большинства. Именно поэтому чтение может простоять 2 353 секунды на чужой записи и не оставить в журнале ни одной ошибки блокировки — искать причину идут в план запроса, а её там нет.
6. gzharkoj 596 04.08.26 18:21 Сейчас в теме
(5) RCSI без режима совместимости для 8.3 - это штатный функционал с какой-то очень ранней 8.3. Все-таки для полноты картины приведите все параметры информационной базы, так будет понятно.
7. nedomolkov.ivan 152 04.08.26 19:13 Сейчас в теме
(6) Согласен: RCSI на 8.3 с управляемыми блокировками — штатный поддерживаемый режим, включить его никто не мешал. В этом и смысл разбора: он поддерживается, но по умолчанию выключен, а отдельного решения включать его на этой базе никто не принимал. Дефолт и оказался частью механики.

Что могу привести без выдумки: режим блокировок — управляемый (не автоматический), СУБД MS SQL, is_read_committed_snapshot_on = 0 и snapshot_isolation_state = OFF по sys.databases, статистика снималась из DMV. Точный релиз платформы и режим совместимости наизусть не назову — база заказчика, доступа под рукой сейчас нет, а придумать цифру в таком разборе хуже, чем оставить пробел. Понадобится для сути — уточню и допишу в статью.

У себя это проверяется одной строкой:
SELECT is_read_committed_snapshot_on, snapshot_isolation_state_desc FROM sys.databases WHERE name = DB_NAME();

Встречный вопрос: какой из недостающих параметров, по-вашему, способен изменить картину? Режим совместимости на ожидание чтения за писателем в READ COMMITTED не влияет — если вы видите механизм, где влияет, это интереснее самой статьи.
9. Mouros 92 05.08.26 11:16 Сейчас в теме
кривая архитектура побежденная "оптимизацией запроса" - ну такое
10. nedomolkov.ivan 152 05.08.26 11:19 Сейчас в теме
(8) Вопрос правильный, и буквальная формулировка «сервер 37 минут держит X-блокировки» — не то, что там происходило. Уточню механику.

Никто не держит одну блокировку 37 минут. Писатель — это порции с проведением документов, то есть тысячи коротких транзакций: каждая берёт X на своих строках и отпускает на коммите. А наш читатель — скан среза по всей таблице на десятки миллионов строк. Под READ COMMITTED без RCSI он на каждой прочитанной странице берёт S и упирается в ту транзакцию, которая пишет прямо сейчас. Одно ожидание короткое, их тысячи — сумма и даёт разрыв elapsed/CPU без единого таймаута в журнале: потолок 20 секунд ловит одно долгое ожидание, а не длинную сумму коротких. Отдельный усилитель — эскалация: массовая запись многих строк в одной транзакции поднимает блокировку до уровня таблицы, и тогда широкий скан встаёт целиком до коммита.

А теперь честная дыра в арифметике, раз уж вы копаете именно сюда. Этап массовой записи шёл около 17 минут, а ожиданий у запроса 2 229 секунд — это 37 минут, то есть больше, чем длился сам сосед-писатель. Значит либо он был не единственным, либо перекрытие сложнее, чем «стартует сразу после». Второе объяснение, которое я обязан назвать: часть разрыва может быть вообще не LCK_M_S, а PAGEIOLATCH_SH — широкий скан десятков миллионов строк ещё и физически читает с диска, и это тоже ожидание, а не CPU.

Разделить эти две версии может только разбивка по wait_type, а её у меня нет — я это признал в (3) и повторю здесь. На практике мы разницу не пощупали, потому что обе версии лечатся одним и тем же сужением чтения: и очередь за блокировками, и лишний ввод-вывод исчезают вместе с прочитанным объёмом. Для точности разбора разница есть, для решения задачи её не было.

(9) Тут спорить не с чем, так и есть: срез по всему регистру в шаге, который стартует в тень массовой записи, — это архитектурная кривизна, а не находка для оптимизатора. Разница в цене вопроса: правку запроса можно выпустить в прод за день с побайтовой сверкой результата, переразложение регламента живого ритейлера — это другой проект и другой бюджет. Если у вас есть версия, как эту схему стоило разложить архитектурно, — интересно услышать без иронии, это была бы более полезная статья, чем моя.
11. paulwist 05.08.26 12:03 Сейчас в теме
(10)
Никто не держит одну блокировку 37 минут.


Хех. Ну не надо упрощать :)

(10)
Одно ожидание короткое, их тысячи — сумма и даёт разрыв elapsed/CPU без единого таймаута в журнале:


Собственно, вы даёте ответ, тысячи транзакций - это секунды/десятки секунд, в исследуемом случае.
Задержка ожидания составляют тысячи секунд, те на один/два порядка больше, отсюда вывод, что "писатели" тоже не используют индексный доступ, а тупо сканируют миллионную табличку.
12. nedomolkov.ivan 152 05.08.26 13:58 Сейчас в теме
(11) Справедливо, упрощение есть — и ваш вывод сильнее моей формулировки.

Арифметика действительно не сходится: тысячи коротких ожиданий дают десятки секунд, а не 2 353. Значит либо писательские транзакции не короткие, либо блокировка успела подняться до уровня таблицы — и тогда одно ожидание живёт столько, сколько живёт вся порция.

Различить эти версии несложно, в техножурнале видно и то, и другое: длительность транзакций писателя и само событие эскалации. У меня на руках только вторая половина картины — то, что видел читатель. Длительность писательских транзакций я не мерил, поэтому утверждать «сканируют и они» не стану, хотя ваша версия объясняет разрыв лучше моей.

Доберусь до того контура ещё раз — соберу и допишу.
13. nedomolkov.ivan 152 05.08.26 13:59 Сейчас в теме
(9) Не спорю: архитектура там кривая, и никакой разбор запроса её не выпрямляет.

Оптимизация ничего не победила — она купила время. Убрала блокировку с критичного окна, чтобы причину можно было чинить в плановом порядке, а не в проде под крики. Это разные вещи, и путать их действительно не стоит.
14. sa1m0nn 28 05.08.26 18:00 Сейчас в теме
Мде... Приготовился уже к захватывающему детективу, какие бывают на Инфостарте, например, про дизасемблирование системных библиотек, из-за которых всё залипало, или обход медленной платформенной ВТ остатков с субконтами путём ручной её имитации, а тут

РегистрСведений.Цены.СрезПоследних(, ТипЦен.Код = "000000164")


Ну это дно, товарищи, потраченного на чтение времени жаль...
ccapt; osa92; cleaner_it; 7o2uYXg; imaster; kharuz; SerEvg; Dmitrii D; wuff; +9 Ответить
21. cleaner_it 202 08.08.26 15:24 Сейчас в теме
(14) Зато комментарии интересные)
22. nedomolkov.ivan 152 08.08.26 15:28 Сейчас в теме
(21)
Зато комментарии интересные)

Хаха)
15. nedomolkov.ivan 152 05.08.26 18:10 Сейчас в теме
(14) Упрёк принимаю: детектива про дизассемблер здесь нет, разгадка банальная — срез последних без отбора после массовой записи.

Писал ровно потому, что банальная. Такие вещи стоят в проде годами, пока их ищут среди сложного: из 2 353 секунд elapsed чистой работы было 124, остальное — ожидание, и до замеров вся команда искала причину в логике запроса, а не в том, что он просто стоит. Ценность тут не в находке, а в методе — отделить «медленный» от «стоящего» до того, как начал переписывать.

Про дизассемблирование системных библиотек и ручную имитацию платформенной ВТ остатков — если у вас такое разобрано, дайте ссылку, прочитаю с интересом. Жанр редкий.
16. SoftLeon 32 05.08.26 22:28 Сейчас в теме
Штатная статистика СУБД по этому запросу (доступна любому администратору через консоль сервера — мы читаем её, ничего не меняя):

Показатель Значение
Полное время выполнения (elapsed) 2 353 с (~39 минут)
Процессорное время (CPU) 124 с (~2 минуты)
Доля ожиданий 95 % времени

Иван, опишите, пожалуйста, для начинающих - как получить эту картину без скриптов и служебных запросов?
Спасибо
17. nedomolkov.ivan 152 06.08.26 04:28 Сейчас в теме
(16) Вопрос по делу, и формулировка в статье его заслужила: «через консоль сервера» — упрощение с моей стороны. Консоль кластера 1С показывает сеансы и их «Время СУБД» — это первый сигнал, куда смотреть, но пары elapsed/CPU по конкретному запросу она не даёт. Эта пара живёт на стороне SQL Server. Разложу, что там нажимается мышью, без единого написанного запроса.

1. Монитор активности (Activity Monitor). SSMS → правый клик по серверу → «Монитор активности» → раздел «Последние ресурсоёмкие запросы» (Recent Expensive Queries). Колонки «Средняя длительность» и «ЦП (мс/с)» стоят рядом: это и есть elapsed против CPU. Соседний раздел «Активные ресурсоёмкие запросы» показывает то, что висит прямо сейчас, — им удобно ловить такой скан в момент, когда он стоит.

2. Хранилище запросов (Query Store) — самое близкое к картинке из статьи. SSMS → нужная база → Query Store → отчёт «Запросы, потребляющие больше всего ресурсов». В отчёте переключатель метрики: Duration, CPU Time и отдельно Wait Time. Один и тот же запрос в трёх метриках — разрыв между длительностью и процессорным временем виден глазами, считать ничего не надо. А отчёт «Статистика ожиданий запросов» разложит ожидания по категориям (блокировки, диск, память) — то самое место, где видно, что запрос стоял, а не считал.
Честная оговорка: Query Store надо включить, а это изменение настройки базы (не данных, но всё же изменение) и небольшие накладные расходы на запись статистики. «Ничего не меняя» — уже не про него. Монитор активности в этом смысле чище: работает сразу.

3. Про 95 % ожиданий. Это не отдельный счётчик, а простое деление: 1 − CPU/elapsed = 1 − 124/2353 ≈ 0,95. Любые два числа «сколько шёл» и «сколько считал» дают его сами. Как привычка это полезнее, чем кажется: если CPU близок к elapsed — запрос честно тяжёлый, и чинится он планом, индексами, переписыванием. Если CPU в разы меньше — запрос ждал, и оптимизировать текст бесполезно, надо искать, кого он ждёт.

4. Как связать SQL-запрос с местом в 1С. В SSMS вы увидите таблицы вида _AccumRg12345. Имя метаданных достаётся без SQL — функцией ПолучитьСтруктуруХраненияБазыДанных() из встроенного языка. Если нужен ещё и контекст (какой модуль, какая строка кода), то технологический журнал, событие DBMSSQL: там есть длительность и текст запроса с контекстом вызова. Процессорного времени в ТЖ нет — поэтому пара elapsed/CPU всё равно собирается со стороны СУБД, одним журналом не обойтись.

Права: для монитора активности и Query Store нужен VIEW SERVER STATE на экземпляре SQL Server. У администратора 1С он обычно есть, у пользователя базы — нет, это отдельная выдача.
18. nedomolkov.ivan 152 06.08.26 04:38 Сейчас в теме
(16) И формулировку в самой статье поправил, чтобы следующий читатель не спотыкался: вместо «через консоль сервера» там теперь явно назван монитор активности SQL Server с путём до него, и отдельно сказано, что доля ожиданий — не счётчик, а деление 1 − CPU/elapsed. Спасибо за замечание, оно по делу.
19. SerEvg 06.08.26 10:04 Сейчас в теме
Пора добавлять кнопку "Забанить нейрослоп". Ну это днище конечно, чтобы исправить косяк этого запроса не нужен ни клод, ни SQL, даже замер производительности делать не надо. Делать отбор внутри параметров виртуальной таблицы это так же элементарно как не хардкодить вещи типа ТипЦен.Код = "000000164". И ладно статью нейронка накатала но блин в комментах то хотя бы можно лично с людьми общаться а не пихать комменты ИИшке и копипастить ответы.
ccapt; osa92; cleaner_it; gzharkoj; Vasvas05; sa1m0nn; +6 Ответить
20. DrZombi 318 08.08.26 11:35 Сейчас в теме
Вода
ccapt; sa1m0nn; cleaner_it; +3 Ответить
Для отправки сообщения требуется регистрация/авторизация