Рекомендации по использованию моделей таблиц
В этом документе рассматриваются особенности использования моделей данных Doris: выбор модели, проектирование схемы таблицы и ограничения моделей. Он поможет выбрать подходящую модель и избежать распространённых проблем с производительностью.
Рекомендации по выбору модели данных
Модель данных определяется при создании таблицы, и изменить её нельзя. Поэтому выбор подходящей модели данных имеет решающее значение.
Сравнение трёх моделей
| Модель данных | Сценарии использования | Преимущества | Примечания |
|---|---|---|---|
| Duplicate | Произвольные запросы по любым измерениям | Нет ограничений агрегационной модели; полностью используются преимущества столбцового хранения (читаются только необходимые столбцы) | Предварительная агрегация недоступна (но поддерживается через асинхронные материализованные представления) |
| Unique | Требуется уникальность первичного ключа и обновление данных | Гарантирует уникальность первичного ключа; поддерживает гибкое обновление данных | Предварительная агрегация недоступна (но поддерживается через асинхронные материализованные представления) |
| Aggregate | Отчётные запросы с фиксированными шаблонами | Предварительная агрегация значительно сокращает объём данных, сканируемых и обрабатываемых при выполнении запроса | Запросы count(*) неэффективны; при выполнении запросов с типом агрегации, отличным от объявленного, необходимо учитывать корректность семантики |
Требования к частичному обновлению столбцов
Если вашим бизнес-процессам необходимо частичное обновление столбцов, рекомендации по его использованию приведены в следующих документах:
Рекомендации по типам столбцов при создании таблиц
При создании таблицы соблюдайте следующие правила выбора типов и порядка столбцов:
- Порядок столбцов: столбцы Key должны располагаться перед всеми столбцами Value.
- Предпочитайте целочисленные типы: используйте целочисленные типы везде, где это возможно, поскольку вычисления и поиск по целым числам значительно эффективнее, чем по строкам.
- Используйте минимальный достаточный размер целочисленного типа: при выборе между целочисленными типами разного размера руководствуйтесь принципом «ровно столько, сколько нужно».
- Используйте минимальную достаточную длину строки переменной длины: для типов VARCHAR и STRING придерживайтесь того же принципа «ровно столько, сколько нужно» при выборе длины.
Различия в семантике столбцов Key между моделями
В моделях Duplicate, Aggregate и Unique столбцы Key задаются при создании таблицы, но их семантика различается:
| Модель | Роль столбцов Key |
|---|---|
| Duplicate | Используются только как «столбцы сортировки»; не обеспечивают уникальную идентификацию |
| Aggregate, Unique | Используются и как «столбцы сортировки», и как «столбцы уникальной идентификации»; это столбцы Key в полном смысле слова |
Ограничения модели Aggregate
Модель Aggregate предоставляет пользователям итоговые агрегированные данные. Иными словами, любые данные, которые ещё не были агрегированы (например, данные из двух разных пакетов загрузки), должны быть приведены к согласованному состоянию с помощью определённого механизма, прежде чем они станут доступны пользователям.
Пример обеспечения согласованности
Предположим, что таблица имеет следующую схему:
| Имя столбца | Тип | Тип агрегации | Комментарий |
|---|---|---|---|
| user_id | LARGEINT | Идентификатор пользователя | |
| date | DATE | Дата загрузки данных | |
| cost | BIGINT | SUM | Общие расходы пользователя |
В движке хранения уже содержатся данные из двух пакетов загрузки:
пакет 1
| user_id | date | cost |
|---|---|---|
| 10001 | 2017/11/20 | 50 |
| 10002 | 2017/11/21 | 39 |
пакет 2
| user_id | date | cost |
|---|---|---|
| 10001 | 2017/11/20 | 1 |
| 10001 | 2017/11/21 | 5 |
| 10003 | 2017/11/22 | 22 |
Как видно, данные пользователя 10001 из двух пакетов загрузки ещё не агрегированы. Чтобы пользователи видели только итоговые агрегированные данные, приведённые ниже:
| user_id | date | cost |
|---|---|---|
| 10001 | 2017/11/20 | 51 |
| 10001 | 2017/11/21 | 5 |
| 10002 | 2017/11/21 | 39 |
| 10003 | 2017/11/22 | 22 |
Doris добавляет оператор агрегации в движок выполнения запросов, обеспечивая согласованность данных, доступных пользователям.
Семантическая ловушка в запросах с другим типом агрегации
При выполнении запроса агрегации по столбцу Value с типом агрегации, отличным от объявленного, необходимо учитывать семантику. Например:
SELECT MIN(cost) FROM table;
Результатом будет 5, а не 1.
Затраты на выполнение запросов count(*)
Механизм обеспечения согласованности в модели Aggregate значительно снижает эффективность некоторых запросов. Наиболее характерный пример: запрос count(*):
SELECT COUNT(*) FROM table;
В других базах данных такой запрос возвращает результат очень быстро. Такие подходы, как «подсчёт строк во время загрузки с сохранением полученного количества» или «сканирование только одного столбца при выполнении запроса для определения количества строк», позволяют получить результат с очень небольшими затратами. Однако в модели Aggregate в Doris этот запрос требует значительных ресурсов.
Для приведённых выше данных итоговый результат агрегации выглядит так:
| user_id | date | cost |
|---|---|---|
| 10001 | 2017/11/20 | 51 |
| 10001 | 2017/11/21 | 5 |
| 10002 | 2017/11/21 | 39 |
| 10003 | 2017/11/22 | 22 |
Правильный результат запроса SELECT COUNT(*) FROM table; должен быть равен 4. Однако:
- Сканирование только столбца
user_idс агрегацией во время выполнения запроса даёт3(10001, 10002, 10003), что неверно. - Сканирование только столбца
user_idбез агрегации во время выполнения запроса даёт5(всего 5 строк в обоих пакетах), что неверно.
Чтобы получить правильный результат, необходимо прочитать оба столбца: user_id и date, а затем выполнить агрегацию во время запроса. Только так можно получить правильный результат 4. То есть для запроса count(*) Doris должен просканировать все столбцы AGGREGATE KEY (в данном случае user_id и date) и агрегировать их, чтобы получить семантически корректный результат. При большом числе столбцов агрегации запросу count(*) приходится сканировать значительный объём данных.
Оптимизация производительности count(*)
Если рабочая нагрузка содержит частые запросы count(*), добавьте дополнительный столбец, значение которого всегда равно 1, а тип агрегации задан как SUM, чтобы имитировать count(*). Измените схему таблицы из предыдущего примера следующим образом:
| Имя столбца | Тип | Тип агрегации | Комментарий |
|---|---|---|---|
| user_id | BIGINT | Идентификатор пользователя | |
| date | DATE | Дата загрузки данных | |
| cost | BIGINT | SUM | Общие расходы пользователя |
| count | BIGINT | SUM | Для подсчёта количества строк |
При загрузке данных задавайте для столбца count постоянное значение 1. Тогда:
SELECT COUNT(*) FROM table;
-- equivalent to
SELECT SUM(count) FROM table;
Второй запрос значительно эффективнее первого.
Ограничение использования: необходимо следить за тем, чтобы строки с одинаковыми значениями столбцов AGGREGATE KEY не загружались повторно. В противном случае SELECT SUM(count) FROM table; отражает только количество исходно загруженных строк и больше не эквивалентен SELECT COUNT(*) FROM table;.
Альтернатива: измените тип агрегации столбца count из примера выше на REPLACE, сохранив постоянное значение 1. В этом случае SELECT SUM(count) FROM table; и SELECT COUNT(*) FROM table; дают одинаковый результат, а ограничений на загрузку повторяющихся строк нет.
Реализация merge-on-write в модели Unique
Реализация merge-on-write в модели Unique не имеет описанных выше ограничений модели Aggregate. Для тех же данных merge-on-write добавляет соответствующую delete bitmap к rowset каждой загрузки, чтобы отметить перезаписанные данные.
После загрузки первого пакета:
пакет 1
| user_id | date | cost | бит удаления |
|---|---|---|---|
| 10001 | 2017/11/20 | 50 | FALSE |
| 10002 | 2017/11/21 | 39 | FALSE |
После загрузки второго пакета повторяющиеся строки в первом пакете помечаются как удалённые:
пакет 1
| user_id | date | cost | бит удаления |
|---|---|---|---|
| 10001 | 2017/11/20 | 50 | TRUE |
| 10002 | 2017/11/21 | 39 | FALSE |
пакет 2
| user_id | date | cost | бит удаления |
|---|---|---|---|
| 10001 | 2017/11/20 | 1 | FALSE |
| 10001 | 2017/11/21 | 5 | FALSE |
| 10003 | 2017/11/22 | 22 | FALSE |
При выполнении запроса все строки, помеченные в delete bitmap как удалённые, отфильтровываются, и агрегация данных не требуется. Приведённые выше данные содержат 4 действительные строки, и результат запроса также содержит 4 строки. Поэтому запрос может использовать упомянутый ранее подход с минимальными затратами: «сканировать только один столбец для определения количества строк».
Данные о производительности: в тестовых средах запросы count(*) в реализации merge-on-write модели Unique выполняются более чем в 10 раз быстрее, чем в модели Aggregate.
Модель Duplicate
Модель Duplicate не имеет описанных выше ограничений модели Aggregate. Поскольку в этой модели отсутствует семантика агрегации, запрос count(*) может просканировать любой один столбец, чтобы получить семантически корректный результат.