Search
Write a publication
Pull to refresh

Автоматическое определение параметров хранения в базе данных

Level of difficultyMedium
Reading time7 min
Views567

Предлагается к рассмотрению метод динамического определения форматов хранения и типов индексации в таблицах аналитической базы данных Dimension DB. На основе метаданных из источников данных (SQL-запросов, таблиц или представлений) определяется формат хранения или представления данных, а по текущему распределению данных, автоматически определяется тип индексирования.

Формат хранения данных Dimension DB
Формат хранения данных Dimension DB

Как известно, в нагруженных системах стараются разделять системы по профилю нагрузки. Для оперативных систем OLTP контур, для отчетной нагрузки DWH. Реализовано это бывает по разному, все зависит от решаемых задач, текущих ограничений и наличия доступных ресурсов. Обычно нормализованные схемы данных из OLTP систем переносят в хранилища данных в "плоские" таблицы, для ускорения выполнения запросов.

Данная тема достаточно хорошо изучена, в основном на больших объемах стараются разделять транзакционную и отчетную нагрузку. Есть специализированные аналитические базы данных, например ClickHouse. Нередка гибридизация, 2 в 1 – когда классические системы дорабатываются для решения задач ускорения обработки данных: движки для колоночного хранения данных на диске, преобразование табличного формата в колоночный в оперативной памяти, различные виды горизонтального и вертикального масштабирования.

И в классическом подходе и в гибридном варианте обычно проектируют схему данных, наиболее подходящую для быстрой работы запросов, определяют форматы хранения данных, типы индексов. В нашем случае, для локального хранения данных временных рядов разработана Dimension DB, в которой реализован полностью автоматический подход по определению форматов хранения и типов индексации.

Описание системы

Dimension DB — аналитическая база данных блочно-колоночного типа для хранения данных временных рядов. Написана на языке программирования Java. В качестве backend для локального хранения данных используется Berkley DB Java Edition.

Для доступа к данным временных рядов локально, разработано API для записи и чтения данных, которое обращается к локальному хранилищу данных типа «ключ‑значение» Berkley DB. Для внешних систем хранения данных с доступом через SQL интерфейс, доступно только чтение данных. При работе с внешними источниками данных по JDBC используется автоматическая генерация SQL запросов.

Архитектура Dimension DB
Архитектура Dimension DB

Для записи в базу данных Dimension DB поддерживается несколько вариантов API:

  • Direct — вставка данных в локальную БД с использованием промежуточной Java структуры данных табличного вида;

  • JDBC — вставка данных в локальную БД с использованием данных, получаемых из внешнего источника данных по JDBC;

  • JDBC Batch — пакетная загрузка в локальную БД с использованием данных, получаемых из внешнего источника данных по JDBC, подходит для больших объемов данных;

  • CSV Batch — пакетная загрузка из CSV-файлов, для переноса больших объемов данных из файлов в локальную БД (пока в статусе экспериментальном).

Для чтения данных из Dimension DB используется API, который оптимизирован для соответствующих типов запросов:

  • Stacked — получение данных на основе функций агрегации, таких как COUNT SUM и AVG, по сути расчет моды по измерению за определенный период времени;

  • Gantt — сложный расчёт распределения с использованием агрегации COUNT по двум уровням. В этом случае расчитывается мода для анализа зависимостей и связей между двумя выбранными измерениями;

  • Raw — извлечение исходных данных в табличном формате, для просмотра детальной информации по каждому показателю;

  • BatchResultSet — извлечение исходных данных в табличном формате с построчным доступом, для получения только части строк из результирующего набора в БД.

Метаданные по наборам данных хранятся локально, используются для запросов к источникам данных по JDBC, CSV-файлам и табличным Java структурам backend-a Berkley DB.

В данный момент реализована поддержка следующих БД:

Таблица № 1. Поддерживаемые БД для работы с API в Dimension DB

№ п.п.

Поддержка API Dimension DB

База данных

Тип базы данных

1

Чтение/Запись

Berkley DB

ключ-значение

2

Чтение

ClickHouse

аналитическая

3

Чтение

Oracle

реляционная

4

Чтение

PostgreSQL

реляционная

5

Чтение

Microsoft SQL Server

реляционная

6

Чтение

MySQL

реляционная

7

Чтение

CSV файлы

файловая

Хранение данных

Поддерживаются три формата хранения данных:

  • RAW — данные загружаются в Berkley DB виде ключа Java-типа int для строковых данных и подходящий Java-тип для остальных. Если тип данных размерности int или меньше — то он сохраняется как есть. Если Long, Float или Double — также как и для строковых значений, сохраняется его ключ в БД в специальной справочной структуре;

  • ENUM — хранятся в виде ключа Java-типа byte значения. Карта соответствия того или иного значения byte хранится в заголовке блоке. Значения ключа Java-типа int получается аналогично как и при сохранении данных в формате RAW;

  • HISTOGRAM — сохраняются начальный и конечный индекс повторяющихся данных из диапазона и само значение в виде ключа Java-типа int. Начальный и конечный индекс тоже типа int.

База данных колоночного типа, данные разделены по столбцам таблицы. Внутри колоночного формата, разделение на блоки. Размер блока данных определяется автоматически по входящему набору данных или вручную, при пакетной вставке через параметр batchSize для JDBC Batch и CSV Batch или в DIRECT режиме регулируя объем входящих данных через промежуточную табличную структуру данных (грузим данные небольшими порциями). Дополнительно есть сжатие блочных структур, реализовано с использованием библиотеки snappy-java. Помогает, если где-то ошиблись с определением типа индексирования в ручном режиме или автоматика не сработала как надо.

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

Глобальное и локальное индексирование

Привычная работа администратора базы данных — определение типов данных и структур индексирования в БД. Типы данных, индексы. Для определенного профиля нагрузки нужны свои настройки. Потом смотрим нагрузку, делаем корректировки. Если корректировки невозможны, правим статистики по таблицам, при перекосах в данных собираем гистограммы.

Формат хранения данных Dimension DB
Формат хранения данных Dimension DB

Тут практически тоже самое, но в случае глобального индексирования, за типам данных не следим – вся информация подгружается из метаданных. Определяем только тип индексирования — выбираем из трех вариантов: RAW, ENUM или HISTOGRAM.

Если различающихся значений в данных немного и они распределены достаточно равномерно, то есть низкая кардинальность данных и нет значительных повторяющихся значений — выбираем ENUM.

Также, низкая кардинальность, но есть значительные повторяющиеся диапазоны данных. Для этого типа данных больше подходит HISTOGRAM, храним начало и окончание того или иного диапазона.

Для всех остальных случаев, для данных с высокой кардинальностью – выбираем RAW.

Если что-то сделали неправильно, есть опция сжатия — можно сильно не беспокоится об этом.

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

Сейчас доступны три варианта настройки определения индекса для опции локального индексирования:

  • ON_LOAD — Данные анализируются при первом получении данных, и на их основе определяется тип индексирования по каждому столбцу таблицы;

  • FULL_PASS_ONCE — Данные анализируются при первом получении данных, затем при следующих запусках последовательно анализируется каждый столбец таблицы один раз и на основе этой информации определяется тип индексирования;

  • FULL_PASS_EACH — Тоже же самое что и FULL_PASS_ONCE, только анализируется каждый столбец таблицы при каждом вызове API записи данных. Каждый вызов API - анализируется один столбец и так по кругу.

В первый запуск тип индексирования — RAW, если что‑то поменялось — перестраиваем существующие данные на новый тип индексирования и обновляем статистики, которые хранятся в памяти.

Тестирование производительности

Для тестирования производительности возьмем базу данных заказов такси Нью-Йорка в ClickHouse и загрузим в Dimension DB данные за 2016 год (~ 78 млн. записей). Сравним различные параметры профилей загрузки: время загрузки, объем данных на диске, время на выполнение тестовых запросов к Gantt API для глобального и локального индексирования. Комбинаций будет достаточно много, возьмем для сравнения четыре профиля со сжатием и посмотрим что получается на среднем ПК.

Таблица № 2. Профили загрузки

№ п.п.

TType

IType

AType

Сжатие

Load (min)

Size (GB)

1

TIME_SERIES

GLOBAL

ON_LOAD

true

99,52

9,132

2

TIME_SERIES

LOCAL

ON_LOAD

true

26,12

12,070

3

TIME_SERIES

LOCAL

FULL_PASS_ONCE

true

26,24

11,407

4

TIME_SERIES

LOCAL

FULL_PASS_EACH

true

26,20

11,881

Таблица № 3. Тесты производительности gantt API

Test name

№ 1 ON_LOAD

№ 2 ON_LOAD

№ 3 PASS_ONCE

№ 4 PASS_EACH

1

getGanttRawRaw

14,0 / 10,5

15,8 / 10,6

17,2 / 10,8

16,3 / 10,5

2

getGanttEnumEnum

4,4 / 2,6

11,2 / 8,0

11,3 / 8,2

10,9 / 7,8

3

getGanttHistHist

2,3 / 1,2

13,6 / 9,8

13,9 / 10,2

14,3 / 9,7

4

getGanttHistRaw

9,9 / 7,5

12,4 / 9,2

12,3 / 9,2

12,0 / 8,9

5

getGanttHistEnum

3,4 / 2,1

13,8 / 10,0

13,9 / 10,3

13,6 / 9,9

6

getGanttEnumRaw

9,5 / 7,0

15,5 / 11,3

16,1 / 11,7

15,6 / 12,2

7

getGanttEnumHist

4,2 / 2,7

16,5 / 12,7

16,5 / 12,6

16,5 / 12,1

8

getGanttRawHist

10,7 / 7,8

15,4 / 11,0

15,7 / 11,6

16,0 / 11,4

9

getGanttRawEnum

9,6 / 7,0

15,4 / 11,3

15,6 / 11,2

15,4 / 11,1

В таблице № 3 первое число — время время исполнения в один поток, вторая — в два потока, в секундах.

Тесты Gant API в один поток
Тесты Gant API в один поток
Тесты Gant API в два потока
Тесты Gant API в два потока

Повторить все можно на своем оборудовании, подробности в README и в тестах (см. исходные коды в пакете integration).

Выводы

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

На данный момент в наличии прототип системы, который позволяет достаточно комфортно работать с локальными данными порядка десятков миллионов записей при среднем размере строки около 100 байт.

И тут формулировка «комфортно» — требует пояснения, что имеется в виду. Речь про то, чтобы максимально быстро извлечь данные (насколько это позволяет доступный hardware и текущая реализация software) и представить их в удобном для многомерного анализа данных временных рядов виде (с использованием текущей реализации user interface). Например, из последних интересных обновлений Dimension UI, который использует Dimension DB в качестве backend‑a — это реализация фильтрации.

Скринкаст фильтрации в интерфейсе Dashboard в режиме real-time
Фильтрация в интерфейсе Dashboard в режиме real-time
Фильтрация в интерфейсе Dashboard в режиме real-time
Скринкаст фильтрации в интерфейсе Dashboard в режиме просмотра истории
Фильтрация в интерфейсе Dashboard в режиме просмотра истории
Фильтрация в интерфейсе Dashboard в режиме просмотра истории
Скринкаст фильтрации на Ad-Hoc
Фильтрация в интерфейсе Ad-Hoc
Фильтрация в интерфейсе Ad-Hoc

Да, Dimension DB медленнее чем ClickHouse. Те же запросы на том же среднем ПК выполняются в пределах 500 миллисекунд. Но есть потенциал по ускорению выполнения запросов, снижению объемов обрабатываемых данных. В текущей реализации достаточно большая избыточность в хранении данных, можно использовать более сжатые структуры, где это позволяют данные. Добавить еще форматов хранения (например битовые карты), что позволит уменьшить занимаемый на диск объем данных и ускорить выполнение запросов.

Исходники проекта

Dimension DB — аналитическая база данных блочно-колоночного типа для хранения данных временных рядов.

Если есть вопросы — можно обратиться к документации по проекту. Подробная инструкция по сборке приложения в README.

Разработано при поддержке «Фонда содействия инновациям».

Вроде все, спасибо за внимание!

Tags:
Hubs:
+8
Comments0

Articles