Сжатие данных в базе данных средствами СУБД

На Инфостарте есть статья https://infostart.ru/public/114634/ про то, как навесить триггеры на создание таблиц, чтобы новые таблицы в БД создавались сжатыми. Для существующих баз предлагается загрузить базу из ДТ или сделать ТиИ с реструктуризацией. Предлагаю вашему вниманию скрипт, который сделает это для существующей БД средствами SQL.

Про плюсы и минусы сжатия можно почитать в оригинальной статье //infostart.ru/public/114634/ или на msdn https://msdn.microsoft.com/ru-ru/library/cc280449(v=sql.120).aspx основным плюсом идет экономия ввода вывода и места на диске, основным минусом — существенное увеличение регламентных операций СУБД и небольшое увеличение нагрузки на процессор.

В management studio правой кнопкой мыши на базе, new query, вставляем текст, нажимаем execute (или F5):

DECLARE @Table_catalog NVARCHAR(128)
DECLARE @Table_schema NVARCHAR(128)
DECLARE @Table_name NVARCHAR(128)
DECLARE @Index_Name NVARCHAR(128)

DECLARE @cmd VARCHAR(4000)

-- включение сжатия для таблиц
DECLARE TableNameCursor CURSOR
FOR
SELECT Table_catalog, Table_schema, Table_name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'
ORDER BY Table_catalog, Table_schema, Table_name

OPEN TableNameCursor

FETCH NEXT FROM TableNameCursor INTO @Table_catalog, @Table_schema, @Table_name
WHILE @@fetch_status = 0
BEGIN

PRINT @Table_catalog + '.' + @Table_schema +  '.' + @Table_name
SET @cmd = 'ALTER TABLE [' + @Table_catalog + '].[' + @Table_schema + '].[' + @Table_name + '] REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = PAGE)'
EXEC (@cmd)

FETCH NEXT FROM TableNameCursor INTO @Table_catalog, @Table_schema, @Table_name
END
CLOSE TableNameCursor
DEALLOCATE TableNameCursor

-- включение сжатия для индексов
DECLARE IndexCursor CURSOR
FOR
SELECT  DB_NAME(), schemas.name, tables.name, indexes.name
FROM sys.schemas as schemas inner join sys.tables as tables inner join sys.indexes as indexes on tables.object_id = indexes.object_id on schemas.schema_id = tables.schema_id
ORDER BY schemas.name, tables.name, indexes.name;

OPEN IndexCursor

FETCH NEXT FROM IndexCursor INTO @Table_catalog, @Table_schema, @Table_name, @Index_Name
WHILE @@fetch_status = 0
BEGIN

PRINT @Table_catalog + '.' + @Table_schema + '.' + @Table_name +  ': ' + @Index_Name

SET @cmd = 'ALTER INDEX [' + @Index_Name + '] ON [' + @Table_catalog + '].[' + @Table_schema + '].[' + @Table_name + '] REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = PAGE)'
EXEC (@cmd)

FETCH NEXT FROM IndexCursor INTO @Table_catalog, @Table_schema, @Table_name, @Index_Name
END
CLOSE IndexCursor
DEALLOCATE IndexCursor

-- делаем шринк базы - возвращаем свободное место на диск
SELECT @cmd=(
SELECT 'DBCC SHRINKDATABASE('''+ DB_NAME() + ''')'
)
EXEC (@cmd)

Скрипт выводит в раздел messages ход работы

Для того, чтобы результаты сохранились после реструктуризации, следует добавить триггеры из оригинальной статьи.

Для выключения сжатия надо запустить скрипт, заменив в нем DATA_COMPRESSION = PAGE на DATA_COMPRESSION = NONE

41 Comments

  1. nvv1970

    Описание способа включения сжатия выглядит как рекомендация. Это так?

    При этом не указано, сколько % места это экономит и сколько % нагрузки добавляет.

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

    Reply ↓
  2. nvv1970

    (1) ознакомился с темой подробнее. Вопросы сняты. Крайне заинтригован отзывами. Побежал тестировать сжатие )))

    Reply ↓
  3. Fragster

    (2) Ну что, как тесты?

    Reply ↓
  4. dm.donetsk

    Скрипт отработал, но ничего не изменилось, размер базы не изменился. До запуска 19,9гб, после 19,9Гб. В чем может быть пролема? Делал для отдельной таблицы

    ALTER TABLE [dbo].[TestTable] REBUILD PARTITION = ALL

    WITH (DATA_COMPRESSION = PAGE)

    Результата тоже нет.

    sp_estimate_data_compression_savings расчитывала сжатие, почему его нет после выполнения скрипта?

    Reply ↓
  5. sssss_aaaaa_2011

    (4)Сжатие данных в базе (Сжатие базы) и сжатие файлов базы данных — две большие разницы. И первое не обязывает сервер делать второе.

    Reply ↓
  6. dm.donetsk

    (5) Из описания к статье «основным плюсом идет экономия ввода вывода и места на диске». Я так понимаю сжатие базы необходимо делать отдельно?

    Reply ↓
  7. sssss_aaaaa_2011

    (6)Термин «Сжатие базы» некорректный.

    Есть сжатие данных и сжатие файлов.

    Первое описано в публикации.

    Второе нужно делать только по необходимости. И ни в коем случае не на регулярной основе. И да, отдельно.

    Reply ↓
  8. dm.donetsk

    (7) Благодарю.

    Reply ↓
  9. sssss_aaaaa_2011

    (0)То же самое, но без курсоров и кучи переменных:

    DECLARE @cmd VARCHAR(MAX)

    sel ect @cmd =(

    SELECT

    ‘PRINT »’+Table_catalog + ‘.’ + Table_schema + ‘.’ + Table_name+ »»+CHAR(10) +

    ‘ALT ER TABLE [‘ + Table_catalog + ‘].[‘ + Table_schema + ‘].[‘ + Table_name + ‘] REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = PAGE)’

    +’;’+char(10)

    as «data()»

    FR OM

    INFORMATION_SCHEMA.TABLES WH ERE TABLE_TYPE = ‘BASE TABLE’

    ORDER BY Table_catalog, Table_schema, Table_name

    for xml path(»)

    )

    Exec(@cmd)

    —Print @cmd

    SELECT @cmd =(

    SELECT

    ‘PRINT »’+DB_NAME() + ‘.’ + sch.name + ‘.’ + tabl.name + ‘.’ + ind.name + »»+CHAR(10) +

    ‘ALT ER INDEX [‘ + ind.name + ‘] ON [‘ + DB_NAME() + ‘].[‘ + sch.name + ‘].[‘ + tabl.name + ‘] REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = PAGE)’

    +’;’+char(10) as «data()»

    FR OM

    sys.schemas as sch

    inner join sys.tables as tabl on sch.schema_id = tabl.schema_id

    inner join sys.indexes as ind on tabl.object_id = ind.object_id

    ORDER BY sch.name, tabl.name, ind.name

    for xml path(»)

    )

    Exec(@cmd)

    —Print @cmd

    Не забудьте убрать лишние пробелы, которые вставляет движок форума.

    Reply ↓
  10. Fragster

    (9) неплохо, не знал, что exec работает не только со строками, но и с таблицами.

    Reply ↓
  11. sssss_aaaaa_2011

    (10)Ну здрасте! И где вы там таблицы нашли? 🙂 А закомментированные Print по вашему мнению тоже таблицы выводят? 🙂

    Reply ↓
  12. Fragster

    (11) ну условно если этот select просто выполнить, то результатом будет таблица

    Reply ↓
  13. Fragster

    (8) добавил шринк в конец скрипта

    Reply ↓
  14. sssss_aaaaa_2011

    (12)Вы чем там исполняете? Скрипт генерит 2(две) строки, которые и исполняет. И если ваше средство исполнения запросов даже сообщения от Print выводит как таблицу, то скрипт тут совершенно не при чем.

    Reply ↓
  15. nvv1970

    (3) тесты прекрасны. Сжатие порой существенно — на порядок.

    При этом чтение ускоряется в 2-3 раза точно.

    А вот с записью все грустно. Замедляется раз в 10.

    Для хранения например журналов, истории чего либо, с ассинхронной записью в таблицы — само то.

    За использование сжатия и синхронной записи — расстрел на месте)))

    Reply ↓
  16. Fragster

    (15) >А вот с записью все грустно. Замедляется раз в 10.

    не было дефицита процессора? у меня запись, конечно, замедлилась, но не настолько. Восстановление из .dt в два-три раза дольше стало.

    Reply ↓
  17. nvv1970

    (16) Антон, тесты я точно не в 1с проводил )))

    Точно уже не вспомню условия теста, но делал его на своем ноуте (i5, ssd). Операция — insert в таблицу1 и в таблицу2.

    Примерно аналогичное замедление(+/-) получил на рабочем сервере.

    Reply ↓
  18. Fragster

    (17) а, я думал что как минимум что-то типа перепроведения всех документов за период было.

    Reply ↓
  19. nvv1970

    (18) нееее )))))))) ну это же бред ))))

    Влияние размывается на тысячи аспектов. Какой удельный вес замедления записи в общей операции — одному богу известно. Наиболее точная оценка от 10% до 90% ))

    Reply ↓
  20. Fragster

    (19) ИМХО мерить синтетикой в наших условиях — не совсем то. Самое правильное — тест центр, правда его настраивать задолбаешься 🙂

    Перепроведение хотя бы к реальности ближе.

    Reply ↓
  21. nvv1970

    (20) вообще не понимаю что вы собираетесь мерить, даже ТЦ. И что вам это даст?

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

    Производить какие-то замеры в 1с бессмыслица и дилетантство. Одно поведение — 90% дискового времени, другое — 10%. Куда деть процессорное время, ожидания и т.п. чтобы понять чистое влияние сжатия СУБД?

    Важно в принципе понимать это чистое поведение, чтобы оценить области его применения. И возможно ли хоть где-то его в 1с применять.

    Reply ↓
  22. Fragster

    (21)

    Важно в принципе понимать это чистое поведение

    Да. Но, например, на ssd обновление строки в СУБД занимает перезапись всего 4кб сектора, что со сжатием, что без сжатия. Таким образом запись больше упирается в процессор. На hdd немного по другому, и при обновлении одной строки действительно нужно читать и писать намного больше данных. + профиль нагрузки, создаваемый 1с действительно очень разнообразный. Для розницы с десятком касс — один, для финансистов с аналитическими отчетами в центральной управленческой базе, в которую оперативные данные сливаются по обменам — совсем другой. Даже сами алгоритмы проведения разные по соотношению чтения/записи. По этому я и говорю, что нужно моделировать именно на конкретном оборудовании с конкретным профилем нагрузки. Более-менее неплохо с этим справляется как раз тест центр от 1с, но он очень долго настраивается. А синтетические тесты — зло. Вот в моем случае пользователи даже не заметили ничего, да и показатели апдекс не поплыли.

    Reply ↓
  23. a.ivanov

    вот мой скрипт ))

    EXEC sp_MSforeachtable ‘ALTER TABLE ? REBUILD WITH (DATA_COMPRESSION = PAGE)’

    EXEC sp_MSforeachtable ‘ALTER INDEX ALL ON ? REBUILD WITH (DATA_COMPRESSION = PAGE)’

    Reply ↓
  24. sssss_aaaaa_2011

    (23)Ну, тоже вариант. 🙂

    Reply ↓
  25. EALeXx

    Может кто подскажет при попытке сжатия базы скриптом выдается ошибка:

    Внутренняя ошибка. Буфер, предоставленный для считывания значения столбца, слишком мал.

    База конечно не маленькая но может кто в курсе как можно победить ошибку

    Reply ↓
  26. a.ivanov

    (25)

    Буфер, предоставленный для считывания значения столбца, слишком мал

    Большая таблица и мало памяти? По английски есть текст ошибки?

    Reply ↓
  27. EALeXx

    (26) Таблицы большие база размером 250 гиг по английски ошибки нет пишет только на русском

    Reply ↓
  28. a.ivanov

    (27) а про запуск CHECKDB есть в сообщении?

    Reply ↓
  29. EALeXx

    (28) да в конце было

    Reply ↓
  30. a.ivanov

    (29) Ну так это первым делом и надо сделать. А потом уже дальше смотреть.

    Reply ↓
  31. EALeXx

    (30) ну так это я сделал и уже дважды а что толку не каких ошибок не обнаружено и все равно ошибка та же выдается

    Reply ↓
  32. PerlAmutor

    Это одна из тех технологий, которую нельзя применять бездумно. Первое что стоит сделать — снять статистику по объектам базы данных, а именно по чтению и записи. Если какая-нибудь таблица меняется чаще чем читается, или поровну, то включать сжатие не следует. Если четко видно, что таблица в основном читается, чем пишется, скажем в 80% случаях, то можно и включить.

    Статистика статистикой, но в разрезе 1С мы можем принять решение основываясь на простых фактах: в конфигурации есть объекты, которые никогда не используются, или не будут использоваться. Документы и справочники — обычно не меняются тысячами. А вот некоторые регистры чаще читаются чем пишутся или наоборот. В общем тут нужен по-объектный анализ и включением сжатия применительно к вашим условиям.

    Reply ↓
  33. Fragster

    можно воспользоваться запросом

    SEL ECT  @@ServerName AS ServerName ,
    DB_NAME() AS DBName ,
    OBJECT_NAME(ddius.object_id) AS TableName ,
    SUM(ddius.user_seeks + ddius.user_scans + ddius.user_lookups)
    AS  Reads ,
    SUM(ddius.user_updates) AS Writes ,
    SUM(ddius.user_seeks + ddius.user_scans + ddius.user_lookups
    + ddius.user_updates) AS [Reads&Writes] ,
    ( SEL ECT    DATEDIFF(s, create_date, GETDATE()) / 86400.0
    FR OM      master.sys.databases
    WHERE     name = ‘tempdb’
    ) AS SampleDays ,
    ( SELECT    DATEDIFF(s, create_date, GETDATE()) AS SecoundsRunnig
    FR OM      master.sys.databases
    WHERE     name = ‘tempdb’
    ) AS SampleSeconds
    FR OM    sys.dm_db_index_usage_stats ddius
    INNER JOIN sys.indexes i ON ddius.object_id = i.object_id
    AND i.index_id = ddius.index_id
    WHERE    OBJECTPROPERTY(ddius.object_id, ‘IsUserTable’) = 1
    AND ddius.database_id = DB_ID()
    GROUP BY OBJECT_NAME(ddius.object_id)
    ORDER BY [Reads&Writes] DESC;
    
    GO

    Показать

    для определения соотношения чтения/записи, но даже если запись осуществляется в 10 раз чаще, профит от сжатия может быть. Нужно тетсить. Как правило все равно процессора больше, чем диска.

    Reply ↓
  34. PerlAmutor

    (33) Т.к. форум «ломает» запрос прикреплю его в виде файла, если не возражаете.

    С удивлением обнаружил, что таблица _Reference183 (Справочник.ИдентификаторыОбъектовМетаданных) читается чаще всех после регистра накопления ПрочиеРасходыНезавершенногоПроизводства, и при этом туда 0 записей. RLS похоже.

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

    Reply ↓
  35. МихаилМ

    скрипт не учитывает специфику 1с : некоторые данные сжимаются deflate . смысла сжимать такие таблицы нет.

    Reply ↓
  36. Fragster

    (35) посмотреть эффект можно с помощью sp_estimate_data_compression_savings

    Reply ↓
  37. Aule2

    По идее если мы не отбираем таблицы с разными типами индексов и не задействуем параметры типа on line = onoff все выборки списка таблиц и индексов можно заменить парой строк

    EXEC sp_MSforeachtable ‘ALT ER TABLE ? REBUILD WITH (DATA_COMPRESSION = PAGE, MAXDOP = N)’

    EXEC sp_MSforeachtable ‘ALT ER INDEX ALL ON ? REBUILD WITH (DATA_COMPRESSION = PAGE, MAXDOP = N)’

    А если вы счастливый обладатель 2017 MSSQL то можно указать явно RESUMABLE = ON

    И после шринка обязательно запустить обслуживание индексов, т.к. операция сжатия полностью фрагментирует индекс

    Reply ↓
  38. St0rmik

    День добрый, а может подскажете как расчитать оборудование для базы размером в 1.7тб и в 2500 пользователей? потому что как только начинаю делать сжатие так сразу по cpu проседать начинаю

    Reply ↓
  39. shmax

    Может кому не лень упростит скрипт при помощи которого смогу сжать данные в конкретной таблице.

    Reply ↓
  40. shmax

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

    ALTER TABLE [upp_3].[dbo].[_AccumRg24455] Rebuild partition = all with (data_compression = page)

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

    Reply ↓
  41. shmax

    всё гуд. всё ужалось, спасибо всем 🙂

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *