Как сжать базу MS SQL размером 800 ГБ и синхронизировать данные между копиями

Системный администратор 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Работа с базами данных большого объема (от 500 ГБ и выше) всегда накладывает серьезные ограничения на регламентные операции. Одной из самых трудоемких задач является сжатие базы (shrink), когда физический файл на диске значительно превышает объем реально хранящихся в нем данных. В данной статье мы разберем ситуацию, когда стандартное технологическое окно не позволяет выполнить сжатие за один раз, и проанализируем, стоит ли использовать механизм синхронизации копий или лучше применить поэтапное сжатие «на лево».

Постановка задачи и анализ стратегий

Рассмотрим ситуацию: имеется рабочая база 1С на MS SQL Server объемом около 800 ГБ. После удаления старых данных или планируемого сжатия ожидается уменьшение объема до 200 ГБ. Однако выполнение команды SHRINKDATABASE или SHRINKFILE занимает слишком много времени и блокирует работу пользователей или выполнение бэкапов. Для предварительной очистки табличных частей документов можно использовать инструмент для безопасного логического сжатия базы.

Первоначальная идея автора заключалась в следующем:

  1. Создать копию базы.
  2. В спокойном режиме сжать копию.
  3. Залить в сжатую копию данные, которые успели накопиться в рабочей базе за время сжатия.
  4. Переключить работу пользователей на новую базу.

Проанализируем этот подход. Основная сложность здесь — синхронизация «дельты» данных. Для 1С это можно сделать через планы обмена (РИБ) или выгрузку/загрузку XML. Однако для нетиповых конфигураций это требует привлечения программистов и не гарантирует 100% идентичности данных без тщательной проверки. Поэтому более эффективным решением часто оказывается работа непосредственно с основной базой, но с применением специальных техник.

Шаг 1: Сжатие данных внутри таблиц (Data Compression)

Прежде чем выполнять усечение файла (shrink), необходимо убедиться, что внутри файла базы данных есть свободное место. Если 1С активно использует таблицы, свободного места может не быть. В этом случае выполнение SHRINKFILE не даст никакого результата.

Рассмотрим применение Page Compression (сжатие страниц). Это позволяет уменьшить объем самих данных внутри таблиц. Проанализируем команды для массового перестроения индексов со сжатием:


-- Включаем сжатие страниц для всех таблиц
EXEC sp_MSforeachtable 'ALTER TABLE ? REBUILD WITH (DATA_COMPRESSION = PAGE)'

-- Включаем сжатие страниц для всех индексов
EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REBUILD WITH (DATA_COMPRESSION = PAGE)'

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

Шаг 2: Поэтапное усечение файла (Piecemeal Shrink)

Главная проблема стандартного SHRINKFILE в том, что он пытается переместить все страницы из конца файла в начало за один проход. Если данных много, процесс может длиться десятки часов. Мы можем применить скрипт, который выполняет сжатие минимальными порциями в цикле. Это позволяет останавливать процесс в любое время и не блокирует базу намертво.

Разберем пример такого скрипта для MS SQL Server:


USE [Имя_Вашей_Базы]
GO

-- Имя файла данных (обычно совпадает с именем базы, но проверьте в свойствах)
DECLARE @FileName sysname = N'YouDataBase_Data'; 
-- Вычисляем целевой размер исходя из текущего (уменьшаем на 1% за итерацию)
DECLARE @TargetSize INT = (SELECT 1 + size*8./1024 FROM sys.database_files WHERE name = @FileName);
DECLARE @Factor FLOAT = .999; -- Коэффициент уменьшения

WHILE @TargetSize > 0
BEGIN
    SET @TargetSize *= @Factor;
    
    -- Выполняем усечение до промежуточного размера
    DBCC SHRINKFILE(@FileName, @TargetSize);
    
    -- Формируем сообщение для мониторинга
    DECLARE @msg VARCHAR(200) = 'Сжатие выполнено. Текущий целевой размер: ' 
        + CAST(@TargetSize AS VARCHAR(20)) + ' MB. Время: ' + CAST(CURRENT_TIMESTAMP AS VARCHAR(20));
    
    RAISERROR(@msg, 1, 1) WITH NOWAIT;
    
    -- Пауза между итерациями, чтобы SQL мог обработать другие запросы
    WAITFOR DELAY '00:00:01'; 
END;

Проанализируем работу этого скрипта. Он не пытается сразу сжать 800 ГБ до 200 ГБ. Он говорит серверу: «Сверни файл до 792 ГБ», затем «до 785 ГБ» и так далее. Если процесс зависнет или потребуется прервать его для обслуживания базы, вы просто останавливаете выполнение. Все достигнутые результаты сохранятся.

Шаг 3: Оптимизация процесса и риски

При выполнении сжатия стоит учитывать несколько критических факторов:

Альтернативный метод: Переезд в новую файловую группу

Если «шринк» идет слишком медленно (например, несколько суток), профессиональные администраторы выбирают метод пересоздания объектов. Мы можем создать второй файл данных в новой файловой группе и перенести туда таблицы одну за другой:

  1. Создаем новую файловую группу (Filegroup).
  2. Добавляем в нее новый файл .ndf.
  3. Переносим таблицы командой CREATE INDEX ... WITH (DROP_EXISTING = ON) ON [Новая_Группа].
  4. Когда старый файл .mdf опустеет, мы его удаляем или уменьшаем до минимума.

Этот метод работает быстрее, так как данные пишутся последовательно, и индексы не фрагментируются. Однако он требует наличия свободного места на диске, равного объему данных (в нашем случае еще 200-300 ГБ сверху к существующим 800 ГБ).

Выясним причину «зависания» сжатия

Если скрипт или команда DBCC SHRINKFILE зависла, не спешите паниковать. Рассмотрим, что может происходить:

Во-первых, сервер может встретить очень большую таблицу или индекс, страницы которого трудно переместить из-за блокировок. Во-вторых, может выполняться фоновое задание (например, обновление статистики или бэкап), которое конфликтует со сжатием. Проанализируем ситуацию через монитор активности (Activity Monitor) или системные представления sys.dm_exec_requests, чтобы увидеть тип ожидания (wait_type). Обычно это PAGEIOLATCH_EX (ожидание диска) или LCK_M_... (блокировка).

Заключение

Для базы 1С объемом 800 ГБ оптимальным решением является отказ от идеи синхронизации копий в пользу поэтапного сжатия на живой базе. Мы рекомендуем сначала выполнить DATA_COMPRESSION = PAGE для крупных таблиц в периоды минимальной нагрузки, а затем запустить циклический скрипт SHRINKFILE с небольшим шагом уменьшения. Это обеспечит стабильность работы системы и позволит уложиться в любые временные рамки, так как процесс можно прерывать и возобновлять.

← На главную