Перейти к основному содержимому

[Предварительная версия] Flat JSON

Эта тема знакомит с основными концепциями функции Flat JSON и способами её использования.

Начиная с версии 1.5.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 данных структура данных должна определяться на основе данных времени выполнения, что затрудняет достижение векторизованной оптимизации.
  • При запросе JSON данных необходимо читать полные JSON данные, включая большое количество избыточных полей.

Selena вводит функцию Flat JSON в версии 3.3 для оптимизации производительности запросов JSON данных и снижения стоимости использования JSON.

Что такое Flat JSON?

Основной принцип Flat 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}

Поля a и b существуют в большинстве строк, и их типы данных схожи (оба INT). Поэтому данные полей a и b могут быть прочитаны из JSON данных и сохранены отдельно как два столбца INT. Если запрос предназначен для этих двух столбцов, данные могут быть прочитаны напрямую без необходимости чтения других столбцов, что снижает накладные расходы на обработку JSON структуры во время вычислений.

Примечания по использованию

  • Кластеры shared-nothing поддерживают Flat JSON начиная с версии 1.5.0. Кластеры shared-data поддерживают Flat JSON начиная с версии 1.5.0.
  • Все типы таблиц в Selena поддерживают Flat JSON.
  • Flat JSON совместим с историческими JSON данными. Исторические данные, которые были загружены ранее, не будут перезаписаны после включения Flat JSON. Они будут сосуществовать с выровненными JSON данными.
  • При записи новых данных операция Flat JSON автоматически завершается через Compaction.

В версиях 3.3.0, 3.3.1 и 3.3.2:

  • Во время загрузки данных Flat JSON поддерживает извлечение общих полей и их сохранение как тип JSON, но вывод типов не поддерживается.
  • Будут храниться как извлеченные столбцы, так и исходные JSON данные. Извлеченные данные удаляются при удалении исходных данных.

Начиная с версии 1.5.0:

  • Результаты, извлеченные из Flat JSON, разделяются на общие поля и зарезервированные поля. Когда все JSON схемы согласованы, зарезервированное поле не будет создано.
  • Во время загрузки данных общие поля будут автоматически выводиться как типы BIGINT/LARGEINT/DOUBLE/STRING. Нераспознанные типы будут выводиться как тип JSON. Зарезервированные поля будут храниться как тип JSON.
  • Flat JSON хранит только общие поля и зарезервированные поля и не хранит исходные JSON данные.

Как использовать Flat JSON

  • Для использования Flat JSON необходимо включить динамический параметр BE enable_json_flat (По умолчанию: false). После включения вновь загруженные JSON данные будут автоматически выровнены.

    curl -XPOST http://<be_host>:<be_http_port>/api/update_config?enable_json_flat=true
  • Перед запросом JSON данных включите переменную сессии cbo_prune_json_subfield (По умолчанию: false).

    SET cbo_prune_json_subfield = true;

Проверка работы Flat JSON

  • После загрузки данных вы можете выполнить следующую SQL команду для запроса подполей, извлеченных из соответствующего поля.

    SELECT flat_json_meta(json_column), count(1)
    FROM tableA[_META];
  • Во время запроса данных вы можете просмотреть связанные метрики через Query Profile:

    • PushdownAccessPaths: количество путей, к которым подполя проталкиваются вниз.
    • AccessPathHits: количество попаданий Flat JSON, чьи подметрики выводят попавшие JSON данные.
    • AccessPathUnhits: количество промахов Flat JSON, чьи подметрики выводят пропущенные JSON данные.
    • JsonFlattern: время, используемое для извлечения JSON подполей при наличии промахов Flat JSON.

Пример использования

  1. Включите enable_json_flat.

  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. Включите переменную сессии cbo_prune_json_subfield.

    SET cbo_prune_json_subfield = true;
  5. Включите функцию Query Profile.

    SET enable_profile = true;
  6. Выполните запросы данных.

    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]';
  7. Получите ID последнего запроса.

    SELECT last_query_id();
    +--------------------------------------+
    | last_query_id() |
    +--------------------------------------+
    | a5d0d795-037c-11ef-93ca-00163e13a1ba |
    +--------------------------------------+
  8. Просмотрите Query Profile для проверки метрик, связанных с Flat JSON.

    Вы можете просмотреть профиль этого запроса, используя любой из следующих методов:

    1. Получите профиль, используя функцию get_query_profile.

       SELECT get_query_profile('a5d0d795-037c-11ef-93ca-00163e13a1ba')\G
    2. Просмотрите метрики профиля через веб-страницу.

    Полный профиль длинный, и следующий рисунок показывает только метрики, связанные с Flat JSON.

    flat_json_profile

Другие дополнительные конфигурации BE

Предостережения

  • Включение Flat JSON увеличит время загрузки JSON данных. Чем больше JSON подполей извлекается, тем больше времени потребляется.
  • Системная переменная cbo_prune_json_subfield работает только при попадании Flat JSON. В противном случае могут быть отрицательные прирост производительности.