Этот материал подготовил автор решения Database Compression Tool (DCT), представленного на Инфостарт Маркетплейс. Мы публикуем его от имени компании, чтобы поделиться практической экспертизой наших авторов. В статье разработчик DCT рассказывает, как устроена свертка больших баз 1С, какие ошибки могут привести к нарушению учета и что необходимо учитывать при работе с базами объемом в сотни гигабайт и несколько терабайт. Мнение, выводы и практические рекомендации в материале принадлежат автору решения.

Заявка выглядит буднично: ERP размером 2,3 терабайта, бэкап перестал влезать в ночное окно, место в СХД заканчивается быстрее, чем согласовывается его закупка. Диски можно докупать до бесконечности, но в какой-то момент кто-то произносит слово «свёртка». И тут выясняется, что удалить из работающей учётной системы две трети строк и ничего при этом не сломать заметно сложнее, чем звучит.

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

Терабайт: не запущенный случай, а норма жизни

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

С точки зрения СУБД база 1С выглядит как обычная реляционная база с необычной схемой: несколько тысяч таблиц с именами вида _AccRg12345, почти без foreign key (ссылочную целостность платформа контролирует сама, на своём уровне) и с характерным профилем нагрузки: учётная система пишет всегда, а не удаляет почти никогда. Каждый документ оставляет движения в регистрах, каждая проводка тянет за собой записи субконто, каждый настроенный обмен копит очереди изменений. Штатной уборки исторических данных в платформе нет, так что история просто лежит. Годами.

Куда именно уходит место, можно увидеть, выполнив всего один запрос. MS SQL (запрос смотрит на текущую базу, так что не забудьте про контекст, иначе полюбуетесь на таблицы master):

USE [ваша_база];
GO

SELECT TOP 20
  t.name AS TableName,
  SUM(CASE WHEN i.index_id IN (0,1) THEN p.rows ELSE 0 END) AS RowsCnt,
  CAST(SUM(a.total_pages) * 8 / 1024. AS numeric(12,1)) AS TotalMB,
  CAST(SUM(CASE WHEN i.index_id IN (0,1) THEN a.total_pages ELSE 0 END) * 8 / 1024. AS numeric(12,1)) AS DataMB,
  CAST(SUM(CASE WHEN i.index_id > 1 THEN a.total_pages ELSE 0 END) * 8 / 1024. AS numeric(12,1)) AS IndexMB
FROM sys.tables t
  JOIN sys.indexes i ON t.object_id = i.object_id
  JOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id
  JOIN sys.allocation_units a ON p.partition_id = a.container_id
GROUP BY t.name
ORDER BY SUM(a.total_pages) DESC;

PostgreSQL (здесь USE не существует, контекст задаётся самим подключением к нужной базе):

SELECT c.relname AS table_name,
       pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,
       pg_size_pretty(pg_relation_size(c.oid))       AS data_size,
       pg_size_pretty(pg_indexes_size(c.oid))        AS index_size
FROM pg_class c
  JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname = 'public'
ORDER BY pg_total_relation_size(c.oid) DESC
LIMIT 20;

Запросы вернут технические имена. Перевод на язык 1С:

Таблица

Что хранит

Почему в топе

_AccRg<N>

Проводки регистра бухгалтерии

Каждая проводка за всю жизнь базы

_AccRgED<N>

Субконто проводок

Несколько записей на каждую проводку

_AccRgAT0…3<N>

Итоги по счетам и субконто

Комбинаторика счетов × субконто × периодов

_AccumRg…<N>

Регистры накопления и их итоги

Движения всех товарных и денежных документов

_InfoRg<N>

Регистры сведений

Версии объектов, логи обменов, истории статусов

…ChngR

Регистрация изменений планов обмена

Очереди изменений для узлов обмена

Отдельный привет таблицам с суффиксом ChngR. Если такая таблица набрала десятки миллионов строк, почти наверняка в плане обмена живёт мёртвый узел: филиал давно закрыли, обмен забросили, а платформа прилежно откладывает для него «письма», которые никто никогда не заберёт.

Обратите внимание и на колонку с индексами: у таблиц 1С их по несколько штук, и суммарный объём индексов нередко сопоставим с объёмом самих данных. Это пригодится через минуту.

Плохая новость про DBCC SHRINKFILE

Администратор, у которого кончается место, первым делом пробует сжатие средствами СУБД. Скажем сразу, чтобы сэкономить вам выходные: shrink возвращает операционной системе незанятое место внутри файлов, но сами данные не уменьшает ни на байт. Если 95% файла занято живыми строками, возвращать нечего. VACUUM FULL в PostgreSQL перепишет таблицу компактно, однако с тем же ограничением: он убирает мёртвые кортежи, а не ваши проводки за 2015 год. Реиндексация уберёт фрагментацию, и после массовых удалений она строго обязательна, но это оптимизация структуры, а не сокращение данных.

Хотите минус терабайт? Придётся удалять строки. Весь вопрос в том, какие строки можно удалить из учётной системы так, чтобы она не заметила потери.

Аксиома свёртки

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

Всё, что эту аксиоиму нарушает, называется другим словом: обрезка. При обрезке базы данных движения удаляются без ввода остатков, в базе остаются битые ссылки и некорректные итоги. У обрезки, кстати, есть вполне законная ниша: технические копии для разработчиков и тестовых стендов, где из 800 гигабайт продуктива нужно быстро получить копию в разы меньше, и на консистентность учёта всем плевать. Но в рабочей базе допустима только свёртка.

Сама механика укладывается в три хода:

  1. Срез остатков. По всем регистрам снимаются остатки на дату свёртки и записываются в специальные технические документы на эту же дату.

  2. Удаление истории. Движения и документы старше даты свёртки удаляются.

  3. Физическое сокращение. СУБД сама место не отдаст: файлы сжимаются, индексы перестраиваются, статистика обновляется.

Есть в этой схеме забавный промежуточный момент: сразу после первого этапа остатки в базе задвоены. Старые движения ещё живы, а технические документы с остатками уже проведены. Выглядит пугающе, но является нормой. Задвоение схлопывается на втором этапе, и возврат итогов к исходным значениям как раз и есть та самая проверка инварианта.

На словах просто. Дьявол, как обычно, кроется в деталях – в том, что «снять остатки» для разных видов регистров означает совершенно разные вещи.

У каждого регистра своя правда

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

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

Регистры бухгалтерии устроены деликатнее всего, потому что в них используются проводки – двойные записи. Проводка не может возникнуть из ниоткуда: у дебета обязан быть кредит и наоборот. Как тогда ввести входящий остаток? Через технический «нулевой» счёт (бухгалтеры знают его как счёт 000 из процедуры ввода начальных остатков). Дебетовые остатки корреспондируют с кредитом нулевого счета, а кредитовые наоборот. Забалансовые счета проще: двойная запись их не касается, остатки снимаются прямым срезом. Так что пачка проводок через 000 в свёрнутой базе не мусор, а след корректно выполненной процедуры.

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

«А сверните нам только одну организацию»

Регулярный запрос от заказчиков. Варианты: только старые документы одного вида, только пару самых толстых регистров, только 41-й счёт. Ответ на все четыре варианта одинаковый: нет. И у каждого «нет» своя причина.

По организации. В базе есть документы, которые проводятся сразу по двум организациям (например, передача товаров между ними). Свёртка «наполовину» оставит такой документ частично проведённым: движения по одной организации удалены, по другой живут. Первое же перепроведение такого документа восстановит удалённые движения поверх остатков, которые свёртка уже ввела техническими документами. Поздравляем, остатки задвоены, и заметите вы это не сразу.

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

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

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

Общее правило: корректная свёртка выполняется согласованно, по всей базе, на одну дату. Каждый «хитрый» частичный вариант экономит часы сейчас и стоит недель разбирательств потом.

Теперь инженерия: миллиард строк на удаление

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

DELETE, в отличие от TRUNCATE, полностью пишется в журнал транзакций, и попытка удалить полтаблицы одной транзакцией на MS SQL в модели восстановления Full означает лог размером с удаляемые данные плюс блокировки на всё время операции. Поэтому взрослый процесс свёртки удаляет порциями, а базу на время работ переводит в Simple recovery (разумеется, с полным бэкапом до и после – точка восстановления на время свёртки всё равно одна). В PostgreSQL своя специфика: там за удалением приходит автовакуум, и его настройки на время массовых операций тоже заслуживают внимания.

Вторая засада ждёт в итоговых таблицах. Платформа 1С поддерживает итоги регистров при каждой записи движений, и массовые операции с включёнными итогами означают пересчёт на каждый чих. На время свёртки итоги отключаются, а в конце пересчитываются один раз.

Третья касается порядка финальных операций. Правильная последовательность жёсткая:

  1. сначала данные (свёртка и удаление),

  2. далее shrink

  3. реиндексация,

  4. статистика.

Если перепутать shrink и реиндексацию, получится знаменитый порочный круг: shrink перемешивает страницы и фрагментирует индексы, rebuild строит индексы заново и снова раздувает файл. Администраторы, гонявшие этот цикл ночами, понимают, о чём речь.

И четвёртое: параллелизм. Регистры друг от друга в основном независимы, так что свёртку можно параллелить по таблицам. Но таблицы драматически разные по размеру: одна _AccRgED может весить как половина базы, и наивное «раздать таблицы по потокам поровну» означает, что семь потоков закончат через час и будут смотреть, как восьмой в одиночку жуёт субконто. Балансировка порций между потоками не самая сложная часть задачи, но одна из самых нужных на практике.

Парадокс железа

Интуиция подсказывает отдать тяжёлую массовую обработку самому мощному серверу в стойке. Практика вносит свои коррективы.

Профиль нагрузки свёртки состоит из длинных последовательных чтений и записей, плюс существенной доли однопоточных этапов, где решает не количество ядер, а производительность одного. Начиная примерно с восьми ядер добавление новых почти ничего не даёт, а вот тактовая частота продолжает работать на всех этапах. Оперативной памяти при этом нужно на удивление скромно: порядка 16-20 ГБ на каждые 500 ГБ (при условии оптимально построенных запросов к данным).

Узким горлышком почти всегда оказывается диск. Ориентир для комфортной работы: от 1000 МБ/с последовательного чтения и записи, то есть нормальный NVMe. Отсюда парадокс, который поначалу веселит заказчиков: рабочая станция с высокочастотным процессором и NVMe сворачивает копию базы быстрее, чем солидный сервер с 128 ядрами на 3 ГГц и СХД, заточенной под случайный доступ маленькими блоками.

Файловых баз это касается отдельно: сворачивать их можно, но из-за особенностей файловой архитектуры процесс идёт кратно медленнее клиент-серверного варианта. Если в планах и переезд на SQL, и свёртка, то лучше сначала переезд, потом свёртка. Не наоборот.

Что получается на живых базах

Несколько результатов с реальных продуктивов (замеры наши, конфигурации клиентские):

Конфигурация

Было

Стало

Сжатие

Время

1С:ERP 2.5

2,3 ТБ

1,2 ТБ

47%

37 ч

1С:УПП 1.3

1,3 ТБ

341 ГБ

73%

16 ч

1С:УТ 10.3

1,1 ТБ

402 ГБ

64%

21 ч

1С:Бухгалтерия 3.0

457 ГБ

210 ГБ

54%

4 ч

1С:КА 2.5

416 ГБ

163 ГБ

61%

6 ч

Разброс процентов не случаен, результат зависит от того, какую долю базы занимает история движений, а какую справочники, вложения и прочие несворачиваемые данные. И это примеры, результаты могут отличаться: УТ бывает и на 3 ТБ, а ERP и на 300 ГБ.

Два ограничения, о которых стоит сказать честно, потому что маркетинг о них обычно молчит:

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

  2. Свёртка уменьшает данные, и всё. Она не перепишет кривой запрос в доработанном отчёте, не вылечит отставшую статистику и не заменит регламентное обслуживание СУБД. Если база тормозила из-за кода, после свёртки она будет тормозить из-за кода, просто сама база станет компактнее.

Куда мы всё это упаковали

Всю описанную механику, от анализа структуры конфигурации до финальной реиндексации, мы собрали в Database Compression Tool (сокращённо DCT) – внешнюю обработку 1С, которая выполняет свёртку конвейером из десятка с лишним шагов. Инструмент сам определяет виды и подтипы регистров по структуре базы, вводит остатки по правилам каждого вида, удаляет историю порциями в несколько потоков и заканчивает сжатием файлов и обслуживанием индексов. Встроенная сверка итогов «до/после» закрывает тот самый инвариант – расхождения видно сразу, а не через месяц в оборотке.

DCT работает на любых конфигурациях (типовые, доработанные, самописные, в т.ч. с расширениями), потому что не привязан ни к конкретной конфигурации, ни к БСП. Поддерживаются MS SQL Server, PostgreSQL (включая сборки для 1С) и файловые базы; всё исполняется on-premise, данные не покидают контур компании, для многих клиентов из госсектора и банков – это решающий аргумент. Из ограничений: серверы 1С на Linux пока не поддерживаются (Все вышеперечисленые СУБД на Linux поддерживаются), базы размещенные на 1С:Fresh тоже, а регистры расчёта инструмент принципиально не трогает (почему, вы уже знаете).

Проверить, сколько высвободится места именно в вашей базе, можно до покупки инструмента: бесплатная демоверсия DCT строит прогноз сжимаемости и показывает отчёты анализа на ваших данных. Продукт продаётся на маркетплейсе Инфостарта: страница продукта, лицензии от 24 900 ₽.

Если будете делать сами

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

  • полный бэкап до начала и архивная копия несвёрнутой базы навсегда (история теперь живёт только там);

  • зафиксируйте итоги всех регистров до свёртки (без эталона сверять будет не с чем);

  • проверьте, что обработка корректно различает остаточные и оборотные регистры накопления и все четыре подтипа регистров сведений;

  • не удаляйте документы с актуальными записями подчинённых непериодических регистров, либо разработайте агрегирующий все эти записи технический документ;

  • не трогайте регистры расчёта;

  • не рискуйте с частичными свёртками: вся база, одна дата;

  • массовые удаления делайте порциями, итоги на время работ отключите, для MS SQL продумайте модель восстановления;

  • финал строго по порядку: данные → shrink → реиндексация → статистика;

  • сверка итогов «до/после» как обязательный последний шаг, а не опция.

И главное: сперва прогоните весь процесс на копии. Сколько бы раз свёртка ни была отработана, продуктив – не место для первого дубля.

Комментарии (0)