Перейти к основному содержимому
Версия: 2.0.x

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

  1. Измените конфигурацию BE: enable_json_flat, которая по умолчанию false до v2.0.0. Для методов изменения см. Configure BE parameters.

  2. Включите функцию pruning FE:

    SET GLOBAL cbo_prune_json_subfield = true;

Включение функции Flat JSON на уровне таблицы

Установка свойств, связанных с Flat JSON, на уровне таблицы поддерживается начиная с v2.0.0.

  1. При создании таблицы вы можете установить 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");
  2. Включите функцию 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 перед созданием таблицы.

  1. Включите функцию (см. другие разделы)

  2. Создайте таблицу со столбцами 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}'));
  3. Просмотрите извлеченные подстолбцы для столбца 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)"] |
    +---------------------------------------------------------------------------------------------------------------------------+
  4. Выполните запросы данных.

    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]';
  5. Просмотрите метрики, связанные с 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

Ограничения функции

  • Все типы таблиц в 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.