Добрый день, коллеги. В этой статье я бы хотел затронуть тему таблиц с типом Row. Этот тип таблиц для многих администраторов баз данных, долгое время оставался наиболее естественным типом, так сказать типом по умолчанию. Таблицы типа COLUMN в основнов встречались в хранилищах данных (Data Warehouse), то есть базах данных с преобладающей нагрузкой типа OLAP.
Основная идея инженеров компании SAP при разработке базы данных HANA, было объединение двух миров OLTP и OLAP приложений. В результате таблицы с колоночным хранением в базе данных HANA, стали таблицами по умолчанию, но несмотря на преимущества колоночных таблиц в большом количестве сценариев, база данных HANA продолжает использовать строковые таблицы. Об особенностях использования такого типа таблиц и пойдёт речь в этой статье.
Таблицы с типом Row хранятся в специальной области памяти, которая называется shared memory. При запуске базы данных эта область полностью загружается в память и остается там всё время, пока база данных находится в рабочем состоянии.
В таблицах с типом Row, все данные размещаются в строках друг за другом, что облегчает доступ ко всем строкам таблицы. А вот с доступом, ко всем значениям столбца немного сложнее, поскольку эти значения не могут быть переданы из основной памяти в CPU с той же эффективностью, как в случае хранения по столбцам. Компрессия данных при таком типе хранения также менее эффективна.
Классический подход для реляционных баз данных, в которых данные хранятся в табличном формате, заключается в хранении, напоминающем логическую структуру таблицы. Каждая запись храниться как объединенный кусок значений каждой колонки таблицы. Ниже представлена таблица с классическим типом хранения.
Пример таблицы с классическим хранениемЭтот дизайн имеет очевидное преимущество, заключающееся в прямом мэппинге представления логической таблицы и операций которые с ней выполняются на фактические манипуляции с данными, которые происходят в памяти. Такое представление проще для разработчиков базы данных и администраторов DBA.
Представление Row-store таблицы в памятиУ такого представления данных есть и свой недостаток, заключается он в том, что для многих операций в базе данных такой подход не очень эффективен. База данных не может получить прямой доступ к определенным колонкам таблицы. Вместо этого страницы с данными необходимо передать CPU для сканирования хранящихся строк по порядку, по идентификатору условияwhereдля определенных столбцов.
Пример сканирования строк в таблицах с типом Row и Column storeЭти данные перемещаются из основной памяти (main memory) в кэш CPU, это оказывает влияние на производительность, особенно при массовой обработке данных, в таких случаях как хранилище данных и аналитические запросы. В то время как для смешанного типа нагрузки, при условии доступа к одной или нескольким записям со всеми колонками, этот метод хранения является предпочтительным, так как требования трансформации данных от внутреннего хранилища к внешнему представлению (также называемое проекцией) для клиента базы данных минимальны.
В базе данных HANA, таблицы с типом Row-store имеют целый ряд ограничений:
-
таблицы не могут быть секционированы
-
отсутствует алгоритм для сжатия таблиц
-
к колонке в такой таблице не может быть предоставлен отдельный доступ, а также параллельный доступ.
-
таблица не может быть выгружена из памяти
-
если таблица с строковым типом не может быть полностью загружена в память, сервер базы данных не сможет запуститься
-
для построения большинства информационных моделей, таблицы с этим типом не могут быть использована напрямую.
Для row-store таблиц, доступно два типа индекса: классический b-tree индекс и cpb+-tree (сжатый префикс b-tree) индекс который оптимизирован для обработки символьных индексных ключей в памяти. Несмотря на то, что есть возможность выбрать тип используемого индекса, обычно в этом нет необходимости. SAP HANA будет использовать cpb+-tree тип определенный по полю или комбинации полей типа string, binary string, или decimal. Для всех остальных типов, будет создан классический b-tree индекс. Индексы по row-store таблицам не сохраняются в БД на постоянной основе, вместо этого они пересоздаются каждый раз при загрузке таблицы в память (запуске базы данных).
Управление многоверсионной конкуренцией (Multiversion Concurrency Control)
Multiversion Concurrency Control (MVCC) это концепция, которая гарантирует консистентность транзакционных данных путем изоляции транзакций которые получают доступ к одним и тем же данным в одно и то же время. Для того чтобы это было реализовано параллельно, сохраняется несколько версий записи. Проблемы с MVCC обычно связаны с большим количеством активных версий записи. Старые версии время от времени должны удаляться для того, чтобы освободить память.
Колоночные и строковые таблицы реализуют это функцию совершенно по-разному.
Для row store таблиц, каждая измененная страница сначала копируется и размещается в цепочке версий страниц, где каждая версия имеет состояние данных на определенную операцию commit. Эти цепочки страниц хранятся в виртуальном контейнере в памяти, который называется undo. Представление для мониторинга M_UNDO_CLEANUP_FILES показывает детальную информацию по этим внутренним виртуальным файлам-контейнерам.
За удаление старых версий записи отвечает Garbage Collector. Сборка мусора происходит после операции commit, а также периодически (по умолчанию каждый час). Сборщик мусора может удалить лишь те старые версии записей, для которых транзакции обновления были завершены (была выполнена операция commit или rollback). В случае, если транзакция изменяет десятки или тысячи строк без фиксации изменений (операции commit), вы можете попасть в ситуацию, когда большой объем избыточных данных необходимо хранить в основной памяти (main memory), так как в базе данных будут храниться десятки тысяч блокированных версий записей и новых активных версий записи. Я уже наблюдал картину, когда по большим таблицам в памяти хранилось до 8 млн. версий записей.
Если обраться к представлению с потоками (например M_SERVICE_THREADS), то в поле Thread Type периодическая сборка мусора будет обозначаться как MVCCGarbageCollector. Для того, чтобы найти транзакции, которые зафиксировали свои изменения, необходимо в представлении потоков, сделать выборку по условиям THREAD_TYPE=SqlExecutor и THREAD_METHOD=CommitTrans.
Реорганизация Row-store области
Реорганизация Row-store области представляет собой процесс дефрагментации сегментов памяти с целью более компактного хранения. Несмотря на значительные улучшения в самом процессе реорганизации, который претерпел существенные изменения начиная еще с HANA 1.0, сама по себе процедура всё еще остается проблемной. Об этом я расскажу ниже, а сейчас немного общей информации.
Пространство Row store увеличивается путем выделения сегментов памяти размером 64 Мб. Сегмент внутренне разделен на страницы фиксированного размера. Когда row-store таблице требуется больше памяти для хранения записей, для неё выделяются свободные страницы из существующего сегмента. Если свободных страниц в сегменте больше нет, выделяется новый сегмент.
Удаление большого числа записей может привести к появлению разрозненных сегментов. В этом случае может помочь реорганизация области row store с целью более компактного хранения в памяти. Страницы из разрозненных сегментов переносятся в другие сегменты, в результате освобождается свободное пространство. SAP рекомендует выполнять реорганизацию Row Store пространства при условии, что его размер превышает 10Gb при этом свободных блоков более 30%. В базе данных HANA доступно два режима реорганизации пространства ONLINE и OFFLINE.
ONLINE реорганизация стала доступна с версии HANA 1.0 SPS8. С тех пор этот режим постоянно улучшается с целью снижения влияния на бизнес процессы и повышения эффективности хранения в области Row store. До версии SAP HANA 2.0 SPS3 включительно, при проведении онлайн реорганизации, на таблицы участвующие в реорганизации устанавливалась эксклюзивная блокировка, в результате чего, изменения записей по этим таблицам были невозможны. Начиная с версии SAP HANA 2.0 SPS4 в процессе реорганизации произошли изменения, теперь при онлайн реорганизации блокировка устанавливается на уровне строки, а не таблицы целиком. В соответствии с рекомендациями SAP online реорганизация необходимо запускать в период минимальной нагрузки для того, чтобы добиться лучшего результата. Именно в этом и кроется основная претензия к этому режиму реорганизации, он не позволяет добиться такого же результата, как в случае OFFLINE реорганизации.
До версии HANA 2.0 SPS4, компания SAP рекомендовала использовать именно OFFLINE режим, для получения лучшего результата. При этом OFFLINE режим несёт в себе один существенный недостаток, логически вытекающий из его названия. Систему работающую в режиме 24х7 для реорганизации row-store пространства никто останавливать не будет. По этому, обычно, OFFLINE реорганизацию совмещают с другими работами, требующими останова системы.
Внимание! OFFLINE реорганизация может существенно увеличить время запуска базы данных HANA.
Начина я с версии SAP HANA 2.0 SPS4 стал доступен режим автоматической ONLINE реорганизации. По умолчанию раз в час запускается проверка на фрагментированность области row store. Если показатель полезного использования памяти менее 60% от размера области row-store, запускается автоматическая реорганизация. Пороговое значение фрагментированности, а также частота запуска проверки может быть изменена. Подробнее о автоматическом режиме можно прочитать в ноте 2789255 - Automatic Online Row Store Reorganization.
На этом я бы хотел завершить краткий обзор таблиц с типом хранения Row. Несмотря на преимущества колоночных таблиц, в базе данных HANA продолжают использоваться и строковые таблицы. В основном такие таблицы используются как технические или настроечные с относительно небольшим количеством записей, при этом основная роль отдана таблицам с колоночным типом хранения.