Добрый день. Меня зовут Сергей, я работаю системным архитектором в компании "РЕД СОФТ". Как я уже писал в прошлой статье, одна из проблем при эксплуатации СУБД Ред База Данных и Firebird — это уменьшение размера файла, выполнить которое возможно исключительно через backup и restore. Это долгие операции и даже при всех возможных оптимизациях будут занимать несколько часов или даже суток.

В большинстве случаев нет необходимости в уменьшении размера БД. Свободное место будет использоваться при вставке данных, а также для хранения старых, но ещё активных версий данных (В Firebird используется механизм MVCC).

Однако в некоторых случаях это сделать полезно (или вам просто очень хочется). Например:

  • Со временем БД стала занимать 80% выделенного диска. 60% данных выгрузили в архивную БД на другом носителе, а БД продолжает занимать 80% диска. Не видно результата, это напрягает людей, которые занимаются сопровождением. Доступное место есть, но сколько его, нельзя увидеть простой командой, нужно собирать статистику по БД и анализировать. А хочется видеть сколько места доступно здесь и сейчас.

  • В информационной системе собираются копии физических баз (с использованием nbackup) и объединяются в кластер. Полученный массив данных применяется для сбора статистики и аналитики. Базы используются в основном на чтение. В таком случае полезно максимально уменьшить размер БД, сократив таким образом требования к объему дисковых накопителей.

Структура файла БД

Кратко рассмотрим как хранятся данные в файле БД. Файл разделен на страницы. Размер страниц может быть 4096, 8192, 16384 байт (32768 байт начиная с 4-й версии Firebird). Страницы могут быть 10 типов:

  1. заголовочная страница БД (database header page)

  2. страница учёта страниц (page inventory page)

  3. страница учёта транзакций (transaction inventory page)

  4. страница указателей (pointer page)

  5. страница данных (data page)

  6. корневая страница индекса (index root page)

  7. страница b-tree индекса (index B-tree page)

  8. страница для хранения BLOB (BLOB data page)

  9. страница генераторов (gen-ids)

  10. страница учёта SCN (SCN's inventory page)

В заголовочной странице содержится ключевая информация о БД - размер страницы, версия структуры файла (ODS), старейшая транзакция, старейшая активная транзакция, следующая транзакция, дата создания, используемый криптоплагин, флаги, GUID и другая информация о БД.

Страница состоит из одинакового для всех типов страниц - заголовка и части, зависимой от типа страницы.

Рис. 1. Заголовок страницы.
Рис. 1. Заголовок страницы.
 Рис. 2. Страница данных.
 Рис. 2. Страница данных.
Рис. 3. Страница b-tree индекса.
Рис. 3. Страница b-tree индекса.
Рис. 4. Страница BLOB.
Рис. 4. Страница BLOB.

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

Подробнее со структурой страниц можно ознакомиться в статье [1] или в исходном коде СУБД Firebird [2].

Структура ФС и SSD

Файловая система обычно располагается на блочном устройстве и выравнивает начало файла по блоку файловой системы, который в свою очередь выровнен по блоку дискового устройства.

Твердотельные диски в представлении операционной системы – это блочное устройство с размером блока 512 или 4096 байт. Может быть и другой размер блока, но встречается редко.

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

Многие файловые системы имеют поддержку trim/discard и при освобождении блока отправляют команду trim накопителю. В Linux есть системный вызов fallocate(int fd, int mode, off_t offset, off_t len) - предварительное выделение или освобождение дискового пространства для файла. При вызове с флагами FALLOC_FL_PUNCH_HOLE и FALLOC_FL_KEEP_SIZE драйвер файловой системы заполняет указанный интервал нулями, не обрезая размер файла. Фактически блоки не выделяются, а при чтении возвращаются нули. По ATA интерфейсу отправляется команда trim, уведомляющая накопитель о том, какие блоки данных не используются и их можно не хранить физически.

Идея

Итак, что мы имеем:

1) В файле БД есть неиспользуемое место.

2) Есть файловая система с поддержкой trim.

3) Есть твердотельный накопитель с поддержкой trim.

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

Есть два способа определения неиспользуемых участков в файле БД:

1) Проанализировать PIP (page inventory page) страницу учёта страниц.

2) Проанализировать страницы. Анализировать достаточно страницы трех типов - данных, b-tree индексов и BLOB, в крупных БД они составляют более 99,9% от общего количества страниц.

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

pluck

В программе pluck была реализована вышеописанная идея. Программа доступна по ссылке https://github.com/neozx/pluck.

При реализации были учтены следующие особенности:

  • Программа не работает с движком Firebird, а совершает действия напрямую с файлом. Следовательно, необходимо гарантировать неизменность файла при работе программы. Это можно достичь двумя способами: полной блокировкой БД (shutdown full) или переводом БД в режим физического бекапа утилитой nbackup. При втором способе БД остается доступна для работы как на чтение, так и на запись.

  • Анализируются страницы трех типов: данных, индексов и BLOB. Анализировать страницы других типов нет смысла, так как их количество незначительное.

Программа работает следующим образом. По умолчанию она запускается в режиме анализа. На первом этапе анализируются PIP-страницы, определяются пустые страницы в файле БД. На втором этапе анализируется содержимое страниц, определяются пусты блоки. В конце работы выводится статистика по количеству страниц и блоков.

При запуске в режим очистки (ключ -t) программа проверяет блокировку БД. Работа также производится в два этапа. Сначала осуществляется поиск свободных страниц, затем идет поиск свободных блоков на странице.

Поддерживаются структуры файлов (ODS) используемых в Ред Базе Данных 2.6 и 3.0, а также Firebird версии 2, 3, 4.

Также в программе есть следующие ограничения:

  • Многофайловые БД не поддерживаются. Поддержка многофайловых БД в Firebird была сделана для обхода ограничения в размере БД максимальным размером файла в файловой системе. Для современных файловых систем проблема неактуальна.

  • Шифрованные БД Ред Баз Данных 2.6 не поддерживаются, т.к. шифруются все страницы и освобождать там нечего.

  • Шифрованные БД Firebird 3 и 4 версии поддерживаются только на 1 этапе.

  • Программа работает только в ОС Linux в котором реализован вызов fallocate с флагами FALLOC_FL_PUNCH_HOLE и FALLOC_FL_KEEP_SIZE. Реализовано в ядре 3.0 и старше, так что будет работать в любом дистрибутиве не старше 10 лет.\

Итог

На практике получены следующие результаты.

1) На БД сделали backup, а затем restore с ключом -USE_ALL_SPACE. После применения утилиты количество выделенных блоков уменьшилось на 2-3%.

2) БД находящиеся в эксплуатации 2-3 года. После применения утилиты количество выделенных блоков уменьшилось на 8-15%.

3) БД из которой была удалена таблица занимающая 20% от БД (Индексы, даты и BLOB, без учета системных страниц). После применения утилиты количество выделенных блоков уменьшилось на 25%.

Время работы зависит от производительности дисковой подсистемы. Время обработки БД размером 335ГиБ составило 2,5 минуты. Время backup & restore этой базы 2 часа с использованием многопоточного бекапа и рестора.

Так как файл становится разреженным, его размер не уменьшается, но уменьшается количество блоков ФС, выделенных этому файлу. Следовательно, уменьшается размер занимаемый файлом на ФС.

Вызов fallocate возвращает 0 в случае, если ошибки не произошло, но это не всегда значит, что блок был освобожден. По факту количество освобожденных блоков будет меньше.

Также стоит отметить, что получить разреженный файл можно выполнив команду cp с ключом --sparse=always, тогда пустоты в файле не будут занимать блоков.

Список литературы

[1] Norman Dunbar, Inside a Firebird Database

[2] https://github.com/FirebirdSQL/firebird/blob/master/src/jrd/ods.h

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