Flat JSON
Эта статья описывает базовую концепцию Flat JSON и как использовать эту функцию.
Начиная с версии 2.2.0, Selena поддерживает тип данных JSON, чтобы обеспечить более гибкое хранение данных. Однако при запросе JSON большинство сценариев не включает непосредственное чтение всех данных JSON, а скорее доступ к данным по указанным путям. Например:
-- Сохранить необходимые поля в логах как фиксированные поля, а другие поля, которые часто меняются с бизнесом, упаковать как JSON.
SELECT
time,
event,
user,
get_json_string(remain_json, "$.from_system"),
get_json_string(remain_json, "$.tag")
FROM logs;
Из-за особой природы типа JSON его производительность в запросах не так хороша, как у стандартных типов (INT, STRING и т.д.). Причины включают:
- Накладные расходы хранения: JSON является полуструктурированным типом, который требует хранения информации о структуре каждой строки, что приводит к высокому использованию хранилища и низкой эффективности сжатия.
- Сложность запроса: Запросы должны определять структуры данных на основе данных времени выполнения, что затрудняет достижение оптимизации векторизованного выполнения.
- Избыточные данные: Запросы должны читать все данные JSON, которые включают много избыточных полей.
Selena вводит функцию Flat JSON для улучшения эффективности запросов данных JSON и снижения сложности использования JSON.
- Эта функция доступна начиная с версии 3.3.0. Она отключена по умолчанию до v2.0.0 и должна быть включена вручную. Начиная с v2.0.0, она включена по умолчанию.
Что такое Flat JSON
Основной принцип Flat JSON заключается в обнаружении данных JSON во время загрузки и извлечении общих полей из данных JSON для хранения в качестве данных стандартного типа. При запросе JSON эти общие поля оптимизируют скорость запроса JSON. Пример данных:
1, {"a": 1, "b": 21, "c": 3, "d": 4}
2, {"a": 2, "b": 22, "d": 4}
3, {"a": 3, "b": 23, "d": [1, 2, 3, 4]}
4, {"a": 4, "b": 24, "d": null}
5, {"a": 5, "b": 25, "d": null}
6, {"c": 6, "d": 1}
При загрузке вышеуказанных данных JSON поля a и b присутствуют в большинстве данных JSON и имеют схожие типы данных (оба INT). Поэтому данные полей a и b могут быть извлечены из JSON и сохранены отдельно как два столбца INT. Когда эти два столбца используются в запросах, их данные могут быть непосредственно прочитаны без необходимости обработки дополнительных полей JSON, что снижает вычислительные накладные расходы обработки структур JSON.
Включение Flat JSON
Flat JSON включен глобально по умолчанию начиная с v2.0.0. Для версий ранее v2.0.0 вы должны включить его вручную.
Начиная с v2.0.0, эта функция может быть настроена на уровне таблицы.
Включение для версий ранее v2.0.0
-
Измените конфигурацию BE:
enable_json_flat, которая по умолчаниюfalseдо v2.0.0. Для методов изменения см. Configure BE parameters. -
Включите функцию pruning FE:
SET GLOBAL cbo_prune_json_subfield = true;
Включение функции Flat JSON на уровне таблицы
Установка свойств, связанных с Flat JSON, на уровне таблицы поддерживается начиная с v2.0.0.
-
При создании таблицы вы можете установить
flat_json.enableи другие свойства, связанные с Flat JSON. Подробные инструкции см. в CREATE TABLE.Альтернативно, вы можете установить эти свойства с помощью ALTER TABLE.
Пример:
ALTER TABLE t1 SET ("flat_json.enable" = "true");
ALTER TABLE t1 SET ("flat_json.null.factor" = "0.1");
ALTER TABLE t1 SET ("flat_json.sparsity.factor" = "0.8");
ALTER TABLE t1 SET ("flat_json.column.max" = "90"); -
Включите функцию pruning FE:
SET GLOBAL cbo_prune_json_subfield = true;
Проверка эффективности Flat JSON
После загрузки данных вы можете запросить извлеченные подстолбцы соответствующего столбца:
SELECT flat_json_meta(<json_column>)
FROM <table_name>[_META_];
Вы можете проверить, получает ли выполняемый запрос преимущества от оптимизации Flat JSON, через Query Profile, наблюдая следующие метрики:
PushdownAccessPaths: Количество путей подполей, проталкиваемых в хранилище.AccessPathHits: Количество попаданий подполей Flat JSON с подробной информацией о конкретном попадании JSON.AccessPathUnhits: Количество промахов подполей Flat JSON с подробной информацией о конкретном промахе JSON.JsonFlattern: Время, затраченное на извлечение подстолбцов на месте, когда Flat JSON не попадает.
Пример использования
-
Метод 1: Настройка свойств Flat JSON при создании таблицы со столбцами JSON. Поддерживается начиная с v2.0.0.
CREATE TABLE `t1` (
`k1` int,
`k2` JSON,
`k3` VARCHAR(20),
`k4` JSON
)
DUPLICATE KEY(`k1`)
COMMENT "OLAP"
DISTRIBUTED BY HASH(`k1`) BUCKETS 2
PROPERTIES (
"replication_num" = "3",
"flat_json.enable" = "true",
"flat_json.null.factor" = "0.5",
"flat_json.sparsity.factor" = "0.5",
"flat_json.column.max" = "50");
INSERT INTO t1 (k1,k2) VALUES
(11,parse_json('{"str":"test_flat_json","Integer":123456,"Double":3.14158,"Object":{"c":"d"},"arr":[10,20,30],"Bool":false,"null":null}')),
(15,parse_json('{"str":"test_str0","Integer":11,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(15,parse_json('{"str":"test_str1","Integer":111,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(15,parse_json('{"str":"test_str2","Integer":222,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(15,parse_json('{"str":"test_str2","Integer":222,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(16,parse_json('{"str":"test_str3","Integer":333,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(17,parse_json('{"str":"test_str3","Integer":333,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(18,parse_json('{"str":"test_str5","Integer":444,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(19,parse_json('{"str":"test_str6","Integer":444,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(20,parse_json('{"str":"test_str6","Integer":444,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')); -
Метод 2: Включение Flat JSON перед созданием таблицы.
-
Включите функцию (см. другие разделы)
-
Создайте таблицу со столбцами JSON. В этом примере используйте INSERT INTO для загрузки данных JSON в таблицу.
CREATE TABLE `t1` (
`k1` int,
`k2` JSON,
`k3` VARCHAR(20),
`k4` JSON
)
DUPLICATE KEY(`k1`)
COMMENT "OLAP"
DISTRIBUTED BY HASH(`k1`) BUCKETS 2
PROPERTIES ("replication_num" = "3");
INSERT INTO t1 (k1,k2) VALUES
(11,parse_json('{"str":"test_flat_json","Integer":123456,"Double":3.14158,"Object":{"c":"d"},"arr":[10,20,30],"Bool":false,"null":null}')),
(15,parse_json('{"str":"test_str0","Integer":11,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(15,parse_json('{"str":"test_str1","Integer":111,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(15,parse_json('{"str":"test_str2","Integer":222,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(15,parse_json('{"str":"test_str2","Integer":222,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(16,parse_json('{"str":"test_str3","Integer":333,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(17,parse_json('{"str":"test_str3","Integer":333,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(18,parse_json('{"str":"test_str5","Integer":444,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(19,parse_json('{"str":"test_str6","Integer":444,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')),
(20,parse_json('{"str":"test_str6","Integer":444,"Double":3.14,"Object":{"a":"b"},"arr":[1,2,3],"Bool":true,"null":null}')); -
Просмотрите извлеченные подстолбцы для столбца
k2.SELECT flat_json_meta(k2) FROM t1[_META_];
+---------------------------------------------------------------------------------------------------------------------------+
| flat_json_meta(k2) |
+---------------------------------------------------------------------------------------------------------------------------+
| ["nulls(TINYINT)","Integer(BIGINT)","Double(DOUBLE)","str(VARCHAR)","Bool(JSON)","Object(JSON)","arr(JSON)","null(JSON)"] |
+---------------------------------------------------------------------------------------------------------------------------+ -
Выполните запросы данных.
SELECT * FROM t1;
SELECT get_json_string(k2,'\$.Integer') FROM t1 WHERE k2->'str' = 'test_flat_json';
SELECT get_json_string(k2,'\$.Double') FROM t1 WHERE k2->'Integer' = 123456;
SELECT get_json_string(k2,'\$.Object') FROM t1 WHERE k2->'Double' = 3.14158;
SELECT get_json_string(k2,'\$.arr') FROM t1 WHERE k2->'Object' = to_json(map{'c':'d'});
SELECT get_json_string(k2,'\$.Bool') FROM t1 WHERE k2->'arr' = '[10,20,30]'; -
Просмотрите метрики, связанные с Flat JSON, в Query Profile
PushdownAccessPaths: 2
- Table: t1
- AccessPathHits: 2
- __MAX_OF_AccessPathHits: 1
- __MIN_OF_AccessPathHits: 1
- /k2: 2
- __MAX_OF_/k2: 1
- __MIN_OF_/k2: 1
- AccessPathUnhits: 0
- JsonFlattern: 0ns
Связанные переменные и конфигурации
Переменные сессии
cbo_json_v2_rewrite(по умолчанию: true): Включает перезапись путей JSON v2, чтобы выражения типаget_json_*могли быть переписаны в прямой доступ к подстолбцам Flat JSON, открывая проталкивание предикатов и обрезку столбцов.cbo_json_v2_dict_opt(по умолчанию: true): Включает оптимизацию словаря низкой мощности для расширенных строковых подстолбцов Flat JSON, произведенных перезаписью, что может ускорить строковые выражения, GROUP BY и JOIN.
Пример:
SET cbo_json_v2_rewrite = true;
SET cbo_json_v2_dict_opt = true;
Конфигурации BE
- json_flat_null_factor
- json_flat_column_max
- json_flat_sparsity_factor
- enable_compaction_flat_json
- enable_lazy_dynamic_flat_json
Ограничения функции
- Все типы таблиц в Selena поддерживают Flat JSON.
- Совместима с историческими данными без необходимости повторного импорта. Исторические данные будут сосуществовать с данными, сглаженными с помощью Flat JSON.
- Исторические данные не будут автоматически применять оптимизацию Flat JSON, если не будут загружены новые данные или не произойдет Compaction.
- Включение Flat JSON увеличит время загрузки JSON. Чем больше JSON извлекается, тем дольше это занимает.
- Flat JSON может поддерживать только материализацию общих ключей в JSON Objects, а не ключей в JSON Arrays.
- Flat JSON не изменяет метод сортировки данных, поэтому производительность запросов и степень сжатия данных все еще будут зависеть от сортировки данных. Для достижения оптимальной производительности могут потребоваться дополнительные настройки сортировки данных.
Примечания к версиям
Кластеры Selena shared-nothing поддерживают Flat JSON начиная с v1.5.2, а кластеры shared-data поддерживают его начиная с v1.5.2.
В версиях v1.5.2:
- При загрузке данных поддерживается извлечение общих полей и их раздельное хранение как типов JSON без определения типа.
- Хранятся как извлеченные столбцы, так и исходные данные JSON. Извлеченные данные будут удалены вместе с исходными данными.
Начиная с версии v1.5.2:
- Результаты, извлеченные Flat JSON, делятся на общие столбцы и столбцы зарезервированных полей. Когда все схемы JSON согласованы, столбцы зарезервированных полей не генерируются.
- Flat JSON хранит только столбцы общих полей и столбцы зарезервированных полей, без дополнительного хранения исходных данных JSON.
- При загрузке данных общие поля будут автоматически определять типы как BIGINT/LARGEINT/DOUBLE/STRING. Нераспознанные типы будут определены как типы JSON, а столбцы зарезервированных полей будут храниться как типы JSON.