欢迎参与 8 月 1 日中午 11 点的线上分享,了解 GreptimeDB 联合处理指标和日志的最新方案! 👉🏻 点击加入

Skip to content
On this page
技术
2024-12-12

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

本文将介绍 GreptimeDB v0.10 的 JSON 类型功能,通过 MySQL 协议示例,展示如何轻松存取链路追踪等半结构化数据。

背景与 JSON 类型介绍

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

json
{
  "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
      }
    }
  ]
}

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

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

JSON 类型例子

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

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

sql
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)
    );

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

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

尝试插入数据:

sql
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 数据

查询表:

sql
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 函数来帮助处理 JSON 类型。

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

sql
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 类型转为字符串:

sql
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 中的特定字段,例如:

sql
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 类型字段的兼容性:

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

接下来使用 json_get_stringjson_get_int 分别来查询:

sql
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 函数来简化数据处理等。


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/

加入我们的社区

获取 Greptime 最新更新,并与其他用户讨论。