暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

支持 JSON 类型,灵活应对复杂数据结构 — GreptimeDB v0.10 新功能详解 #2

GreptimeDB 2024-12-13
107

背景与 JSON 类型介绍

GreptimeDB v0.10 (最新版本 v0.11.0 已发布)引入了 JSON 类型支持,完善了半结构化和结构化数据的支持能力。GreptimeDB 的 Schemaless 模型本身类似 MongoDB,在通过 gRPC、Prometheus Remote Write 等协议写入的时候支持动态的建表和自动加列 [1],可灵活应对数据结构的不确定性。不过在很多场景下,很多列都的是结构化的,比如典型的 OpenTelemetry 里的 Traces [2] (链路追踪):

{
  "name""hello",
  "context": {
    "trace_id""5b8aa5a2d2c872e8321cf37308d69df2",
    "span_id""051581bf3cb55c13"
  },
  "parent_id"null,
  "start_time""2022-04-29T18:52:58.114201Z",
  "end_time""2022-04-29T18:52:58.114687Z",
  "attributes": {
    "http.route""some_route1"
  },
  "events": [
    {
      "name""Guten Tag!",
      "timestamp""2022-04-29T18:52:58.114561Z",
      "attributes": {
        "event_attributes"1
      }
    }
  ]
}

其中 context
attributes
events
都是 JSON 对象或数组。GreptimeDB 的目标是成为一个能统一处理指标、日志、事件以及链路的时序数据库,就必须支持 JSON 类型。

本文以 GreptimeDB 的 MySQL 协议 [3]支持来举例,并且假设读者已安装 GreptimeDB v0.10 版本。安装指南请阅读文档 [4]。

JSON 类型例子

使用指南:创建表与插入数据

首先,你可以创建表的时候将某个列指定为 JSON 类型:

 CREATE TABLE traces(
   `name` STRING
    context JSON
    parent_id STRING
    start_time TIMESTAMP
    end_time TIMESTAMP
    attributes JSON
    events JSON
    TIME INDEX (start_time), 
    PRIMARY KEY(`name`, parent_id, context)
    );

我们这里将 context
attribtues
events
都设置为了 JSON 类型,时间索引设置为 start_time,将 name
parent_id
context
加入 Primary Key,也就是作为标签列。

由于 name
是保留关键字,因此需要使用反引号进行转义。

尝试插入数据:

INSERT INTO traces
  VALUES
  (
     'hello',
     '{
       "trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
       "span_id": "051581bf3cb55c13"
      }'
,
      null,
      '2022-04-29T18:52:58.114201Z'::TIMESTAMP,
      '2022-04-29T18:52:58.114687Z'::TIMESTAMP,
      '{
        "http.route": "some_route1"
      }'
,
      '[
         {
           "name": "Guten Tag!",
           "timestamp": "2022-04-29T18:52:58.114561Z",
           "attributes": {
             "event_attributes": 1
           }
         }
      ]'
);

用户可以直接将 JSON 字符串作为 JSON 类型写入, GreptimeDB 会自动转换为 JSON 类型。

查询与处理 JSON 数据

查询表:

mysql> SELECT * FROM traces\G;
*************************** 1. row ***************************
      name: hello
   context: {"span_id":"051581bf3cb55c13","trace_id":"5b8aa5a2d2c872e8321cf37308d69df2"}
 parent_id: NULL
start_time: 2022-04-29 18:52:58.114000
  end_time: 2022-04-29 18:52:58.114000
attributes: {"http.route":"some_route1"}
    events: [{"attributes":{"event_attributes":1},"name":"Guten Tag!","timestamp":"2022-04-29T18:52:58.114561Z"}]
1 row in set (0.01 sec)

ERROR:
No query specified

对于表的数据查询,因为可以从表的 schema 中拿到类型信息,所以查询的时候会自动将 JSON 类型转化为字符串来展示,如上所示。GreptimeDB 提供了一些 JSON 函数 [5]来帮助处理 JSON 类型。

比如 parse_json
,可以将字符串解析为 JSON:

mysql> select parse_json('{"span_id":"051581bf3cb55c13","trace_id":"5b8aa5a2d2c872e8321cf37308d69df2"}');
+--------------------------------------------------------------------------------------------------+
| parse_json(Utf8("{"span_id":"051581bf3cb55c13","trace_id":"5b8aa5a2d2c872e8321cf37308d69df2"}")) |
+--------------------------------------------------------------------------------------------------+
| @          span_idtrace_id051581bf3cb55c135b8aa5a2d2c872e8321cf37308d69df2              |
+--------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

由于 GreptimeDB 底层是用 binary 类型来存储 JSON, parse_json
返回的是一个 binary 的结果。

json_to_string
函数将 JSON 类型转为字符串:

mysql> select json_to_string(parse_json('{"span_id":"051581bf3cb55c13","trace_id":"5b8aa5a2d2c872e8321cf37308d69df2"}'));
+------------------------------------------------------------------------------------------------------------------+
| json_to_string(parse_json(Utf8("{"span_id":"051581bf3cb55c13","trace_id":"5b8aa5a2d2c872e8321cf37308d69df2"}"))) |
+------------------------------------------------------------------------------------------------------------------+
| {"span_id":"051581bf3cb55c13","trace_id":"5b8aa5a2d2c872e8321cf37308d69df2"}                                     |
+------------------------------------------------------------------------------------------------------------------+

通过 json_get_
系列函数提取 JSON 中的特定字段,例如:

mysql> SELECT json_get_string(context'trace_id'FROM traces;
+--------------------------------------------------+
| json_get_string(traces.context,Utf8("trace_id")) |
+--------------------------------------------------+
| 5b8aa5a2d2c872e8321cf37308d69df2                 |
+--------------------------------------------------+
1 row in set (0.02 sec)


mysql> select json_get_string(parse_json('{"span_id":"051581bf3cb55c13","trace_id":"5b8aa5a2d2c872e8321cf37308d69df2"}'), 'span_id');
+-----------------------------------------------------------------------------------------------------------------------------------+
| json_get_string(parse_json(Utf8("{"span_id":"051581bf3cb55c13","trace_id":"5b8aa5a2d2c872e8321cf37308d69df2"}")),Utf8("span_id")) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| 051581bf3cb55c13                                                                                                                  |
+-----------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

看到这里,熟悉 MongoDB 的朋友可能要问,我们能不能直接用 SELECT context.trace_id FROM traces
的方式来查询?

很遗憾暂时不行,因为我们没法保证 context.trace_id
的类型在所有列中是一致的,而 GreptimeDB SQL 的查询引擎和返回结果却是强类型的,这就需要用户明确来指定下希望返回的类型,不符合该类型的字段将尝试类型转换,或者返回 null

我们插入一条 trace_id
为整数的数据,测试其对不同 JSON 类型字段的兼容性:

INSERT INTO traces(namecontext,start_time) 
  values ('world''{"trace_id": 999}',now());

接下来使用 json_get_string
json_get_int
分别来查询:

mysql> SELECT json_get_string(context'trace_id'FROM traces;
+--------------------------------------------------+
| json_get_string(traces.context,Utf8("trace_id")) |
+--------------------------------------------------+
| 999                                              |
| 5b8aa5a2d2c872e8321cf37308d69df2                 |
+--------------------------------------------------+
2 rows in set (0.02 sec)


mysql> SELECT json_get_int(context'trace_id'FROM traces;
+-----------------------------------------------+
| json_get_int(traces.context,Utf8("trace_id")) |
+-----------------------------------------------+
|                                          NULL |
|                                           999 |
+-----------------------------------------------+
2 rows in set (0.02 sec)

结果显示,json_get_string
能将两行结果都正确返回,且整数 999 转为字符串并正常返回,而 json_get_int
则正确提取了整型字段 999
,但由于另一行的 trace_id
为字符串,无法转换,因此返回了 NULL

总结

GreptimeDB v0.10 引入了 JSON 类型后,通过 JSON 函数,增强了半结构化和结构化数据的存取能力。未来, GreptimeDB 也将进一步加强对 JSON 类型的支持,例如允许对 JSON 内部分字段建立倒排索引来加速查询,引入类似 Databend JQ [6] 函数来简化数据处理等。

Reference

[1] https://docs.greptime.com/zh/user-guide/ingest-data/overview/
[2] https://opentelemetry.io/docs/concepts/signals/traces/
[3] https://docs.greptime.com/zh/user-guide/protocols/mysql/
[4] https://docs.greptime.com/zh/getting-started/installation/overview/
[5] https://docs.greptime.com/zh/reference/sql/functions/json/
[6] https://docs.databend.com/sql/sql-functions/semi-structured-functions/jq

v0.10 系列文章的合集:

后续文章发布中,敬请期待!

关于 Greptime

Greptime 格睿科技专注于为可观测、物联网及车联网等领域提供实时、高效的数据存储和分析服务,帮助客户挖掘数据的深层价值。目前基于云原生的时序数据库 GreptimeDB 已经衍生出多款适合不同用户的解决方案,更多信息或 demo 展示请联系下方小助手(微信号:greptime)。

欢迎对开源感兴趣的朋友们参与贡献和讨论,从带有 good first issue 标签的 issue 开始你的开源之旅吧~期待在开源社群里遇见你!添加小助手微信即可加入“技术交流群”与志同道合的朋友们面对面交流哦~

Star us on GitHub Now: https://github.com/GreptimeTeam/greptimedb
官网:https://greptime.cn/
文档:https://docs.greptime.cn/
Twitter: https://twitter.com/Greptime
Slack: https://greptime.com/slack
LinkedIn: https://www.linkedin.com/company/greptime/

往期精彩文章:

点击「阅读原文」,立即体验 GreptimeDB!

文章转载自GreptimeDB,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论