Свёртка, которая не лечит. Часть 2: музей архитектурных ужасов

01.10.26

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

Вторая часть разбора проекта свёртки базы 1С:ERP на 2,5 ТБ. Что на самом деле нагружало сервер: ЭДО, которое раз за разом подписывало уже подписанное, ночное обслуживание, не доходившее до конца очереди, 45 ГБ регистрации изменений для брошенных обменов — и расширение свёртки, на которое уходило около 45 часов процессорного времени в сутки.

Часть 2. Музей архитектурных ужасов: что копилось годами, пока все смотрели на размер базы

Это вторая часть цикла. В первой: производственной компании с базой 2,5 ТБ поставили диагноз «слишком большая, надо сворачивать», подрядчик занялся свёрткой, а первая же приёмка показала, что в присланной базе нет признаков свёртки — зато есть следы прерванного обновления конфигурации. Мы тем временем сняли реальную нагрузку и увидели, что ни одна из причин не связана с объёмом. Пришло время посмотреть внутрь.

Сразу оговорка, чтобы не сложилось неверного впечатления. Ниже — не «что натворил подрядчик по свёртке». Ниже — то, что копилось годами, от разных команд и под разное «срочно, к понедельнику». Что-то писали в спешке, что-то настроили и забыли, что-то, судя по всему, ставили «на пробу» и не выключили.

Подрядчик по свёртке в этом музее занимает ровно один зал — последний в этой части. Зато вход в него самый дорогой.

 

Зал 1. Контур обмена документами, который сошёл с ума

В один прекрасный день электронный документооборот с контрагентами просто умер — на обработку одного цикла уходило больше двух часов. Начинаем копать.

В журнале за один рабочий день — 5 183 ошибки от сервиса ЭДО. Все — на одних и тех же 756 документах, и все с одним и тем же смыслом: «документ в финальном состоянии, изменять нельзя».

Система честно пыталась подписать документы, которые уже давно подписаны, получала отказ, обижалась и через два часа пыталась снова. Так — с прошлого года. Обработка была настроена по расписанию каждые два часа, и каждые два часа совершала одну и ту же бессмысленную работу. Как человек, который каждое утро проверяет, не открылась ли дверь, которую он сам заколотил.

Отдельная находка там же: самый тяжёлый запрос всей базы по данным SQL Server оказался запросом к справочнику сообщений этого же контура — 1,6 миллиарда чтений, до 8 минут на один вызов.

Лечилось одним покрывающим индексом: 750 тысяч чтений на вызов превратились в 4. Не в 4 тысячи — в четыре. Без единой строки кода. Самый дешёвый выигрыш за весь проект, нам даже немного неловко.

Что сделали: привели в порядок статусы зависших документов, чтобы система перестала подписывать уже подписанное, и добавили индекс. Теперь цикл отрабатывает за 2–5 минут.

 

Зал 2. Ночное обслуживание, которое никогда не заканчивалось

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

Задание запускалось каждые пять минут в окне с половины девятого до половины двенадцатого вечера. Один проход длился 47 минут, то есть окно было занято целиком — а очередь работ при этом не убывала.

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

В очереди на пересчёт стояло почти 15 тысяч объектов, обрабатывалось по десять за вызов, а весь план целиком на каждом вызове записывался в журнал. Очередь была отсортирована по размеру, крупные объекты возвращались в неё через два дня — и до хвоста, до мелких, она не доходила никогда. Классическое голодание: те, кто в начале очереди, обслуживаются ежедневно, те, кто в конце, — никогда.

За 1 801 запуск журнал накопил 25,8 миллиона строк, служебная база регламента разрослась примерно до 25 гигабайт.

Замер по-честному, по факту изменений, показал: реально требовали обновления 666 статистик. Ещё 34 650 пересчитывались впустую каждый день. А 55 тысяч «никогда не обновлявшихся» оказались статистиками на пустых таблицах — их и обновлять было не к чему.

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

Что сделали: переписали отбор — по факту изменений, а не по календарю. Обновление статистики теперь занимает секунды: боевой прогон обработал 664 объекта за 17 секунд, контрольный следом вернул ноль — очередь закрывается за один заход. Журнал очищен и больше не растёт. Обслуживание индексов переписали отдельно — с ограничением по времени, чтобы проход физически не мог залезть в рабочее утро.

 

Зал 3. Мёртвые души

Эту находку мы сделали случайно — измеряли совсем другое, время записи документов маркировки, и упёрлись в цифру, которая не сходилась ни с чем. Оказалось, в базе лежит 45 гигабайт регистрации изменений для обмена: 378 миллионов строк в служебных таблицах.

Стали смотреть, для кого это всё регистрируется. Два плана обмена, две брошенные настройки.

Первая — узел обмена со второй ERP-базой, который кто-то когда-то начал настраивать: запустил первичную выгрузку, не дождался её окончания и ушёл. Узел так и завис в состоянии «Выгрузка данных…», а в графах «отправлено» и «получено» — честное «Никогда».

Платформа с тех пор добросовестно регистрировала для него каждое изменение по трём тысячам объектов: зарегистрировано 370 миллионов изменений, выгружено — 2 911. Если считать это КПД, то он получается меньше одной тысячной процента, и это, пожалуй, худший показатель, который я видел за карьеру.

Вторая настройка — незавершённое обновление конфигурации «через копию», брошенное в ноябре прошлого года со статусом «Настройка не завершена».

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

Сорок пять гигабайт аккуратно сложенных писем адресату, который никогда не придёт. Настройки почистили, данные удалили.

 

Зал 4. Что там с «размером»

Раз уж все смотрели на объём — мы тоже посмотрели, из чего он состоит. Разложили базу по объектам и получили простую арифметику:

  • журнал транзакций SQL — 200 гигабайт, который никто не обрезал;
  • присоединённые файлы — больше 300 гигабайт, и все лежат прямо в базе (об этом отдельно в следующей части, там есть на что посмотреть);
  • регистрация изменений для мёртвых обменов — ещё 45.

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

Остатки регистров накопления — то, что свёртка собственно и режет, — примерно треть объёма. Самая рискованная треть.

И ещё одна деталь — план переноса. Крупнейшие объекты по нему либо не трогались, либо переносились целиком. Заметного уменьшения ждать было неоткуда — и это видно из плана ещё до старта. А цель «уменьшить базу» достигалась и без свёртки: штатными настройками и обслуживанием журнала.

 

Зал 5. Вечный двигатель. Единственный зал подрядчика — и самый дорогой

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

Примерно через неделю база встала колом.

Начав разбираться в чужом коде, мы ахнули. Механизм писал служебную запись на любое действие пользователя — вплоть до каждого штрихкода маркировки. И перед КАЖДОЙ записью делал запрос к этой же служебной таблице: «а нет ли уже такой?»

Индекса под этот поиск не было. Поле со значением было объявлено строкой в две тысячи символов, при том что в него всегда клали 36-символьный идентификатор. Таблица выросла до 1,4 миллиона строк, и SQL Server на каждое действие пользователя пересканировал её целиком.

Итог в цифрах за сутки: 330 тысяч вызовов, 10,9 миллиарда чтений, около 45 часов процессорного времени. Да, в сутках 24 часа. У сервера много ядер — и все они были заняты этим. Одна служебная таблица одного расширения потребляла больше, чем весь остальной топ нагрузки вместе взятый.

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

Мы сняли основную часть индексом (45 часов → около 4), остальное — правка на стороне подрядчика.

 

А теперь — самое неприятное

Всё, что описано выше, — архитектура. Контуры обмена, регламентные задания, чужие расширения. Их можно найти, если сесть и методично читать код и журналы SQL-сервера. Рано или поздно нашёл бы кто-то другой.

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

Продолжение — в части 3: как документ выпуска продукции блокировал сам себя, что бывает, если не поставить галочку, и почему одна и та же болезнь возвращалась ровно каждое воскресенье в семь утра, будто по расписанию.

Случай обезличен, отдельные детали обобщены. Оценки — мнение автора по результатам замеров, а не оценка свёртки как метода или исполнителей в целом.


Технические подробности для коллег

Зал 1 — тяжёлый запрос ЭДО. Лидер Query Store по логическим чтениям во всей базе: 1,62 млрд чтений за 2 153 выполнения, до 481 с на вызов. План: поиск по некластерному индексу (разделитель, дата), затем Key Lookup для проверки строковых условий и сортировка TOP N. Индекс на стороне СУБД под этот запрос дал 481 с → 0,4 мс. Нюанс, о который мы потом споткнулись: индекс, созданный в SQL в обход конфигурации, платформа не знает и молча теряет при реструктуризации таблицы. Такие индексы надо вести в отдельном реестре и проверять после каждого обновления конфигурации.

Зал 2 — отбор статистик. Вместо возраста — sys.dm_db_stats_properties.modification_counter с порогом 5 % строк. Пустые таблицы отсекаются по числу строк (sys.dm_db_partition_stats, row_count для index_id 0 и 1), а не по выделенным страницам: страницы остаются за таблицей и после удаления данных, а статистика пустой таблицы не получает гистограммы и навсегда числится «никогда не обновлявшейся». Очередь — по дате последнего обновления, а не по размеру; в журнал пишется только обрабатываемая порция. Итоговый расклад по 90 319 статистикам: 54 989 — на пустых таблицах, 34 650 — стабильные, 666 — реальная суточная нагрузка, 13 — разовый долг. Автообновление статистики в базе асинхронное, поэтому ночной регламент здесь не формальность, а основной механизм её точности.

Зал 3 — регистрация изменений. Объём считали по таблицам изменений планов обмена через sys.partitions и sys.allocation_units, подтверждали штатной формой «Регистрация изменений для обмена». Признак брошенного узла: состояние «Выгрузка данных…», отправлено и получено — «Никогда», а зарегистрировано на порядки больше, чем выгружено.

Зал 5 — поиск без индекса. Проверка «нет ли уже такой записи» шла сканом всей служебной таблицы: по Query Store около 330 тыс. вызовов, 10,9 млрд логических чтений и около 45 ч CPU в сутки — больше, чем весь остальной топ вместе взятый. Индекс на стороне СУБД под условие поиска снял основную часть, остаток — около 3,8 ч CPU в сутки. Строка на 2 000 символов под 36-символьный GUID — отдельный вопрос к автору расширения.

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

1С:ERP производительность MS SQL Server свертка базы обслуживание индексов статистика планы обмена Query Store

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

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

См. также

HighLoad оптимизация Разработчик 1С 8.3 1С:ERP Управление предприятием 2 Бесплатно (free)

Использование оператора «В» для полей или данных составного типа (например, Регистратор) может приводить к неочевидным проблемам.

10.11.2025    15341    ivanov660    48    

57

HighLoad оптимизация Разработчик 1С:Предприятие 8 1C:ERP Бесплатно (free)

Приведем примеры использования различных в динамических списках и посмотрим, почему это плохо.

18.02.2025    16148    ivanov660    39    

62

HighLoad оптимизация Разработчик Россия Бесплатно (free)

А вы знали, что сервер 1С при соединении с базой на сервере PostgreSQL самостоятельно устанавливает некоторые параметры? Это важно знать при настройке сервера и отладке долгих запросов. Предлагаю разобраться.

27.08.2024    10055    soulner    10    

41

HighLoad оптимизация Технологический журнал Системный администратор Разработчик Бесплатно (free)

Обсудим поиск и разбор причин длительных серверных вызовов CALL, SCALL.

24.06.2024    18768    ivanov660    13    

64

HighLoad оптимизация Разработчик 1С:Предприятие 8 Бесплатно (free)

Метод очень медленно работает, когда параметр приемник содержит намного меньше свойств, чем источник.

06.06.2024    24662    Evg-Lylyk    73    

46

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

Анализ простого плана запроса. Оптимизация нагрузки на ЦП сервера СУБД используя типовые индексы.

13.03.2024    14365    spyke    29    

54
Для отправки сообщения требуется регистрация/авторизация