Свертка информационной базы — задача, которую каждый из нас делал десяток раз: типовая обработка, документы ввода остатков, удаление истории до даты среза. На базе в 50–100 ГБ это рутина. На базе в 2,3 ТБ с 46 миллионами документов и таблицами по 300–430 миллионов строк типовой подход не просто медленный — он математически не помещается ни в какое окно простоя.
В этой статье — методика, которой мы свернули операционную базу крупной сети ломбардов Казахстана (сотни отделений, отраслевая сильно кастомизированная конфигурация, MS SQL Server 2019 Enterprise) с 2 341 до 573 ГБ за одно ночное окно ~5 часов. С нулевым расхождением остатков «до/после» — бит-в-бит по всем регистрам, включая бухгалтерию. Методика не привязана к отрасли: она применима к любой конфигурации, где типовая свертка не влезает в окно.
Почему типовая свертка не работает на терабайтах
Начнём с честного замера, который мы сделали до проектирования. Взяли копию базы и попробовали удалить пакет закрытых документов штатными средствами — «Удаление помеченных объектов» с контролем ссылочной целостности, как положено. Экстраполяция полученной скорости на весь объём дала оценку: только каскад удаления занял бы месяцы. Не часы и не дни — месяцы непрерывной работы.
Причины фундаментальные, и знакомы каждому, кто заглядывал в то, как платформа удаляет объекты:
- Платформенное удаление — построчное и ссылочно-честное. На каждый удаляемый объект — проверка ссылок, событийная модель, транзакционная обвязка. Это правильно для сотни документов и убийственно для 40+ миллионов.
- DELETE на сотнях миллионов строк — это боль СУБД. Построчное журналирование, разрастание журнала транзакций, эскалация блокировок, распухшие индексы, которые всё равно потом перестраивать.
- Пересчёт итогов после массового удаления платформенными средствами на таких объёмах сам по себе занимает часы.
- Проверить результат нечем. Типовой сценарий не даёт инструмента доказать заказчику, что после свертки остатки совпали копейка в копейку, а ссылочная целостность не пострадала. «Вроде всё сходится» на 2,3 ТБ — это не приёмка.
И поверх всего — бизнес-ограничение, которое ломало типовой сценарий концептуально: договор залога живёт годами. Договор, открытый пять лет назад и действующий сегодня, обязан пережить свертку полностью — сам документ, пролонгации, частичные гашения, начисления процентов, кассовые документы, вся история движений. Свертка «всё до даты N сворачиваем» здесь невозможна в принципе: граница проходит не по дате, а по множеству живых цепочек документов. Причём прямого признака «активности» договора в данных не было — его пришлось конструировать.
Архитектура методики: гибрид «логика — в 1С, массы — в SQL»
Ключевое архитектурное решение делит работу по принципу «каждый делает то, что умеет лучше всех»:
- Бизнес-логика — кодом 1С. Расчёт защищаемого множества, генерация документов ввода остатков, проведение, бухгалтерская свертка доработанной типовой обработкой. Всё, где важна корректность с точки зрения платформы: движения, итоги, последовательности, события.
- Массовые операции — оптимизированным T-SQL. Удаление сотен миллионов строк, пересчёт итогов регистров. Всё, где важна пропускная способность.
- Верификация — эталонными механизмами 1С. Каждый результат SQL-трека сверяется с тем, что дала бы платформа: пересчитали итоги в SQL — сверили побитово с платформенным пересчётом на контрольном контуре.
Последний пункт — то, что отличает методику от «давайте просто почистим базу скриптами». SQL без верификации против платформы — это способ быстро получить базу, которой нельзя доверять. Верификация превращает скорость SQL в легальный инструмент.
Второй столп архитектуры — исполнение как оркестрируемый процесс, а не набор скриптов. Ночное окно — это 22 фазы по графу зависимостей под управлением самописного оркестратора: маркеры состояния каждой фазы, heartbeat-контроль, супервизор, возобновление с любой точки после обрыва. Каждая фаза либо идемпотентна (повторный запуск не портит результат), либо защищена числовым гейтом перед необратимым действием. Про оркестратор ниже отдельно.
Этапы: как это устроено внутри
1. Аудит и классификация регистров
Первый деливерабл — таблица по каждому регистру базы: сворачивается / защищается / имеет собственный механизм очистки / не трогаем. Скучно, но именно тут закладываются все дальнейшие гарантии.
Аудит же вскрыл аномалии, о которых заказчик не знал: «фантомные» договоры — след давнего бага проведения. Отдельным треком прошла автоматизированная валидация бухучёта по 78 тысячам проблемных закрытых договоров: по каждому проверено, что баланс сходится в ноль. Сошёлся у 100 % — только это дало право удалять их вместе с остальной историей. Важный принцип: валидация на полной выборке, а не на сэмплах. На сэмплах доказывается «обычно работает», а нам нужно было «работает всегда».
2. Критерий активности: признак, которого нет в данных
Признак «действующий договор» собирался из трёх независимых источников: остатки задолженности, остатки по всем операционным регистрам, срез регистра состояний. Логика объединения — консервативная: договор считается активным, если хотя бы один источник считает его активным.
// Псевдокод. Реальная реализация — запросы 1С по регистрам конфигурации.
АктивныеДоговоры =
Договоры_С_НенулевойЗадолженностью(НаДату = ДатаСреза)
Договоры_С_ОстаткамиПоОперационнымРегистрам(НаДату = ДатаСреза)
Договоры_АктивныеПоСрезуСостояний(НаДату = ДатаСреза)
Три источника — это не перестраховка ради галочки. Каждый из них по отдельности врёт на краевых случаях (тот самый давний баг проведения, «фантомы», незакрытые хвосты), и расхождения между источниками сами по себе стали диагностическим инструментом: каждое расхождение разбиралось до причины, прежде чем множество было зафиксировано. Итог: ~296 тысяч защищаемых договоров из 46 миллионов документов.
3. Метки защиты: транзитивное замыкание
Активный договор — это не один документ. Это цепочка: документы-основания, перезалоги, пролонгации, гашения, кассовые документы, носители остатков. Поэтому от множества активных договоров строится транзитивное замыкание по ссылкам — и материализуется в данных как «метки защиты», которые видит каждый последующий шаг пайплайна:
Защита = АктивныеДоговоры
Повторять:
Новое = ДокументыСвязанныеС(Защита) \ Защита // основания, перезалоги, кассовые, ...
Защита = Защита W46; Новое
Пока Новое X00; W09;
Вышло ~1,5 млн меток защиты. Материализация принципиальна: защищаемое множество вычисляется один раз, фиксируется и дальше используется как данные, а не пересчитывается каждым шагом заново с риском разъехаться.
4. Срез остатков: идемпотентный генератор
Документы-носители остатков генерируются по всем операционным регистрам (только ненулевые сальдо) кодом 1С; бухгалтерская свертка — доработанной типовой обработкой. Ключевое свойство генератора — идемпотентность: повторный запуск не задваивает срез. Каждый документ-носитель детерминированно адресуем (регистр + разрез), и генератор работает в режиме «создать или актуализировать», а не «создать ещё раз».
Зачем? Затем, что на многочасовом пайплайне обрыв сеанса — не гипотеза, а рабочая ситуация. Идемпотентность шага — это право перезапустить его, не разбираясь в три часа ночи, «на чём оно упало и что успело сделать».
5. Пред-чеки: расхождение — стоп
Перед необратимой фазой автоматика сверяет: срез равен остаткам учёта на дату среза по каждому регистру; все кандидаты на удаление — вне защищённого множества. Любое расхождение — стоп всего пайплайна. Не предупреждение в лог, а стоп: числовой гейт, который физически не пускает процесс дальше.
6. Массовый каскад: table swap вместо DELETE
Сердце SQL-трека. Когда удаляется большинство строк таблицы, DELETE — худший из инструментов. Вместо него — table swap: выжившие строки переносятся в новую таблицу, на ней с нуля создаются индексы, затем таблицы меняются местами. Схема паттерна (упрощённо, не код заказчика):
-- 1. Новая таблица той же структуры
SELECT s.*
INTO dbo._DocTable_new
FROM dbo._DocTable s
JOIN dbo._ProtectMarks p ON p.Ref = s._IDRRef; -- выжившие ~ защищённое множество
-- 2. Индексы и ограничения — с нуля, на уже итоговых данных
-- (быстрее, чем поддерживать их во время вставки, и без фрагментации)
-- 3. Подмена имён в одной короткой транзакции
BEGIN TRAN;
EXEC sp_rename 'dbo._DocTable', '_DocTable_old';
EXEC sp_rename 'dbo._DocTable_new', '_DocTable';
COMMIT;
-- _DocTable_old живёт до конца окна как локальная точка отката
На наших замерах swap быстрее DELETE в 5–9 раз — и бесплатным бонусом отдаёт свежепостроенные нефрагментированные индексы. Каскад шёл в 4 параллельных потока (раскладка таблиц по потокам — по замерам с репетиций, чтобы потоки финишировали примерно одновременно). Итог: ~1,4 млрд строк обработано за ~2 часа. Параллельно, независимым треком, — бухгалтерская свертка.
Оговорка для повторяющих: swap требует аккуратности со структурой (все индексы, ограничения и особенности таблиц платформы переносятся точно) и выполняется только на замороженном контуре — см. ниже.
7. Пересчёт итогов — в SQL, сверка — против платформы
Итоги регистров накоплений после каскада пересобираются напрямую в T-SQL — агрегация движений по периодам итогов. Это в 5 раз быстрее платформенного пересчёта (на репетициях: 112 минут → 21). Но правило верификации железное: результат SQL-пересчёта побитово сверялся с эталонным платформенным пересчётом. Совпало — SQL-вариант получает право на прод. Платформа остаётся судьёй; SQL — только исполнитель.
8. Заморозка контура и онлайн-хвост
На окно свертки контур замораживался полностью: регламентные задания (особенно отложенное проведение), интеграции, обмены, блокировка сеансов. Любой из этих механизмов, оставшись живым, способен «дописать» данные посреди каскада и развалить сверку.
А вот некритичные зачистки (осиротевшие элементы справочников, служебные кэши) сознательно вынесены за окно — они шли уже на работающей базе, чанками с автотроттлингом:
Пока ЕстьЧтоЧистить:
УдалитьЧанк(N строк)
Если РостЖурналаТранзакций > Порог
или ЕстьБлокировкиЧужихСессий
или СвободноеМесто < Порог:
Пауза / уменьшить N // процесс сам себя придерживает
Это разделение — «необратимое и целостностное в окно, косметика онлайн» — и позволило уложить простой бизнеса в одну ночь ~5 часов.
Контроль качества: три гейта приёмки
Приёмка — не «открыли отчёты, посмотрели», а три формальных гейта:
| Гейт | Что проверяет |
|---|---|
| Гейт 1 | Контрольный снимок остатков на дату среза «до» == «после» бит-в-бит: все операционные регистры + бухгалтерия + бизнес-метрики |
| Гейт 2 | Целостность цепочек перезалогов: ни одна ссылка истории договоров не потеряна |
| Гейт 3 | Полный скан базы на битые ссылки по 8 500+ ссылочным колонкам схемы: 0 нарушений |
Про гейт 3 стоит сказать отдельно. 8 500 ссылочных колонок — это реальность отраслевой конфигурации, и наивное удаление гарантированно оставляет битые ссылки, которые всплывают в отчётах и проведении месяцами, отравляя жизнь всем. Полный скан — генерируемый по метаданным проход по каждой ссылочной колонке с проверкой, что значение либо пустое, либо указывает на живой объект. Дорого, но однократно — и это единственный способ доказать целостность, а не надеяться на неё.
На продуктиве все три гейта — зелёные с первого прогона. Это не везение, это следствие следующего раздела.
Репетиции и оркестратор
7+ полных end-to-end репетиций на копии продуктива. Каждая репетиция — полный прогон от заморозки до гейтов, со снятием таймингов каждой фазы. Репетиции делали две вещи: ужесточали пайплайн (каждый найденный краевой случай превращался в пред-чек) и ускоряли его — каскад с первой репетиции к финальной: 10 часов → 2 часа; пересчёт итогов: 112 минут → 21.
Финальная репетиция — это фактически генеральный прогон: выход на прод был исполнением сценария, который уже семь раз завершался успешно, с известным таймингом каждого шага.
Оркестратор — 22 фазы по графу зависимостей: маркеры состояния фаз в БД, heartbeat, супервизор с авторестартом, возобновление с произвольной точки. Философия — crash-only design: пайплайн проектируется из предположения, что обрыв может случиться в любой момент, и в любой точке рестарт безопасен — за счёт идемпотентности шагов и числовых гейтов перед необратимыми фазами. Финальные прогоны прошли без единого аварийного рестарта, но право на аварию было заложено в каждую фазу.
Плюс два защитных контура, о которых часто забывают:
- Точки отката перед каждой деструктивной фазой — включая
_old-таблицы после swap до конца окна. - Аудит потребителей данных: перед каждой зачисткой — проверка, кто эти данные читает. Включая автоматизированный разбор кода всех 204 внешних обработок заказчика: внешняя обработка, живущая в базе годами, имеет свойство читать ровно ту таблицу, которую вы считали мёртвой.
Цифры
| Показатель | До | После |
|---|---|---|
| Файл данных на диске | 2 341 ГБ | 573 ГБ (−75 %) |
| Полный бэкап (со сжатием) | ~570 ГБ | 74 ГБ (−87 %) |
| Файл журнала транзакций | 119 ГБ | 8 ГБ |
| Расхождения остатков «до/после» | — | 0 (бит-в-бит по всем регистрам) |
| Битые ссылки после свертки | — | 0 (скан по 8 500+ колонкам) |
| Активные договоры | — | 100 % сохранены с полной историей |
| Простой бизнеса | — | одно ночное окно ~5 часов |
От аудита до продуктива — около месяца. Восстановление тестовых контуров теперь занимает минуты вместо часов — для команды заказчика это, возможно, самый ощутимый ежедневный эффект.
Когда эта методика применима (и когда не нужна)
Не нужна, если база до пары сотен гигабайт, окно простоя не жёсткое, а бизнес-правила отбора укладываются в «всё до даты N». Типовая обработка + аккуратность — и не усложняйте.
Применима, когда сходятся хотя бы два из условий:
- объём от сотен гигабайт, крупнейшие таблицы — от десятков миллионов строк, и контрольный замер показывает, что штатное удаление не помещается в окно;
- критерий «что сохранить» не сводится к дате — живые многолетние цепочки документов (договоры, заказы, проекты), которые обязаны пережить свертку с полной историей;
- цена ошибки высока: регулируемый учёт, требование сверки «до/после» копейка в копейку;
- допустимый простой — одна ночь, и второй попытки не будет.
Методика не зависит от конфигурации — типовая или отраслевая, важна лишь дисциплина: логика в 1С, массы в SQL, верификация против платформы, идемпотентность шагов, числовые гейты, репетиции до скуки. Из всего перечисленного самое дешёвое и самое недооценённое — репетиции: семь прогонов на копии превратили ночь на проде из приключения в исполнение регламента. Приключения хороши в отпуске.
Вступайте в нашу телеграмм-группу Инфостарт