扁平 JSON
本文介绍扁平 JSON 的基本概念以及如何使用此功能。
从 2.2.0 版本开始,StarRocks 支持 JSON 数据类型,以允许更灵活的数据存储。但是,在查询 JSON 时,大多数场景不涉及直接读取整个 JSON 数据,而是访问指定路径中的数据。例如
-- Store required fields in logs as fixed fields, and package other fields that frequently change with business as 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 数据,其中包括许多冗余字段。
StarRocks 引入了扁平 JSON 功能,以提高 JSON 数据查询效率并降低 JSON 的使用复杂度。
- 此功能从 3.3.0 版本开始提供,默认禁用,需要手动启用。
什么是扁平 JSON
扁平 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)。因此,可以从 JSON 中提取字段 a
和 b
的数据,并将其作为两个 INT 列单独存储。当在查询中使用这两列时,可以直接读取其数据,而无需处理额外的 JSON 字段,从而减少了处理 JSON 结构的计算开销。
验证扁平 JSON 是否有效
加载数据后,您可以查询相应列的提取子列
SELECT flat_json_meta(<json_column>)
FROM <table_name>[_META_];
您可以通过 查询 Profile 观察以下指标来验证执行的查询是否受益于扁平 JSON 优化
PushdownAccessPaths
:下推到存储的子字段路径的数量。AccessPathHits
:扁平 JSON 子字段被命中的次数,其中包含有关特定 JSON 命中的详细信息。AccessPathUnhits
:扁平 JSON 子字段未被命中的次数,其中包含有关特定 JSON 未命中的详细信息。JsonFlattern
:未命中扁平 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]'; -
在 查询 Profile 中查看与扁平 JSON 相关的指标
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
功能限制
- StarRocks 中的所有表类型都支持扁平 JSON。
- 与历史数据兼容,无需重新导入。历史数据将与扁平 JSON 展平的数据共存。
- 除非加载新数据或发生 Compaction,否则历史数据不会自动应用扁平 JSON 优化。
- 启用扁平 JSON 会增加加载 JSON 所花费的时间。提取的 JSON 越多,花费的时间越长。
- 扁平 JSON 只能支持物化 JSON 对象中的常用键,而不支持 JSON 数组中的键。
- 扁平 JSON 不会更改数据排序方法,因此查询性能和数据压缩率仍会受到数据排序的影响。要实现最佳性能,可能需要进一步调整数据排序。
版本说明
StarRocks shared-nothing 集群从 v3.3.0 开始支持扁平 JSON,shared-data 集群从 v3.3.3 开始支持。
在 v3.3.0、v3.3.1 和 v3.3.2 版本中
- 加载数据时,它支持提取常用字段并将其作为 JSON 类型单独存储,无需类型推断。
- 提取的列和原始 JSON 数据都会存储。提取的数据将随原始数据一起删除。
从 v3.3.3 版本开始
- 扁平 JSON 提取的结果分为常用列和保留字段列。当所有 JSON Schema 一致时,不会生成保留字段列。
- 扁平 JSON 仅存储常用字段列和保留字段列,而不额外存储原始 JSON 数据。
- 加载数据时,常用字段会自动推断类型为 BIGINT/LARGEINT/DOUBLE/STRING。无法识别的类型将被推断为 JSON 类型,并且保留字段列将存储为 JSON 类型。
启用扁平 JSON 功能(3.4 版本之前)
- 修改 BE 配置:
enable_json_flat
,在 3.4 版本之前默认为false
。有关修改方法,请参阅 配置 BE 参数 - 启用 FE 剪枝功能:
SET GLOBAL cbo_prune_json_subfield = true;