实际上并没有所谓的 JSON(JavaScript 对象表示法)数据库,但有些数据库的设计旨在与 JSON 数据良好配合,或具有强大的 JSON 支持。
JSON 是一种基于文本的数据交换格式,在过去十年中已成为数据输出的通用语言——API 返回 JSON,它用于表示单页应用程序中的状态,并且指标或日志通常以这种格式生成。
什么是 JSON?
JSON 是一种轻量级数据交换格式,易于人类阅读和编写,也易于机器解析和生成。JSON 最初由 Douglas Crockford 在 2000 年代初期指定,其符号源自 JavaScript,迅速普及成为一种与语言无关的格式。到 2000 年代后期,它已成为传输应用程序状态和促进客户端-服务器交互的事实标准。
JSON 文档包含键/值对,其中键是字符串,值可以是字符串、整数、布尔值、数组或对象。下面显示了一个 JSON 文档示例,表示电子商务网站上的订单
{
"orderId": "ORD-12345",
"orderDate": "2023-05-15T14:30:00Z",
"customer": {
"id": "CUST-789",
"name": "Alice Johnson",
"email": "[email protected]"
},
"items": [
{
"productId": "BOOK-001",
"title": "The Great Gatsby",
"price": 12.99,
"quantity": 1
},
{
"productId": "BOOK-002",
"title": "To Kill a Mockingbird",
"price": 14.99,
"quantity": 2
}
],
"totalAmount": 42.97,
"status": "processing"
}
JSON 文档没有模式,这意味着我们可能有另一个文档表示具有略微不同字段的订单。例如,也许我们没有客户的姓名,因此 customer.name
字段将缺失。
JSON 的半结构化特性提供了灵活性,使得修改和扩展数据结构变得容易,而无需更改现有记录。它还支持快速原型设计和敏捷开发。
JSON 数据库示例
现在我们已经了解了 JSON,让我们探讨如何以这种格式存储数据。几种不同类型的数据库可以存储和检索 JSON 格式的数据。在本节中,我们将更多地了解这些数据库。
文档数据库
文档数据库(如 CouchDB 和 MongoDB)在 2000 年代末/2010 年代初兴起。这些数据库以类似 JSON 的文档形式存储数据,从而实现灵活的、无模式的数据建模。它们的主要卖点是用户可以轻松存储 JSON 并检索单个文档。
这使得它们非常适合在交互式、JavaScript 繁重的网页中存储应用程序状态,这些网页随着 jQuery 以及后来的 AngularJS 和 React 的出现而流行起来。像 Twitter 这样的社交媒体网站在同一时期也开始流行,并提供 JSON API 来访问其数据,用户随后将其存储在他们选择的文档数据库中。
尽管 MongoDB 和 CouchDB 是此类别中的早期竞争者,但其他还包括 Couchbase、CosmosDB 和 Firestore。
具有 JSON 支持的关系型数据库
两个最流行的开源关系型数据库 PostgreSQL 和 MySQL 最初没有 JSON 支持,它们花了一段时间才赶上。
PostgreSQL 在 9.2 版本(2012 年 9 月发布)中添加了对 JSON 的原生支持。随后,他们在后续版本中添加了对索引的支持。MySQL 也紧随其后,在 5.7.8 版本(2015 年 8 月发布)中添加了自己的 JSON 数据类型。
这些数据库仍然主要用于以行和列的形式存储数据,并检索单个或少量行。JSON 类型允许它们存储半结构化数据,但它们在处理 JSON 方面没有文档数据库那么多功能。
具有 JSON 支持的实时分析数据库
在 2010 年代后期和 2020 年代初期,出现了实时分析数据库,如 Apache Druid、Apache Pinot、Rockset 和 ClickHouse。这些数据库使用基于列的存储,并专注于优化跨数十亿行或更多行的大规模分析查询。
它们都具有从 JSON 文档中提取模式的功能,如果所有 JSON 文档都具有相同的模式,则效果良好。
然而,对于常见的用例(如可观测性和日志记录),数据通常是半结构化的,这意味着这些数据库也必须添加对 JSON 数据类型的支持。
在存储数据时,它们需要确保以允许快速执行嵌套 JSON 查询的方式存储数据——将数据存储为字符串并将工作推迟到查询时间是不够好的。
ClickHouse 中的 JSON
ClickHouse 在 2022 年 6 月的 22.6 版本中添加了 JSON 数据类型,但此实现存在一些限制,并在 24.8 版本(2024 年 8 月发布)中被新的 JSON 数据类型取代。
新的数据类型解决了以下挑战
- 真正的列式存储 - 为 JSON 数据实现列式存储系统,以实现高效的压缩和快速的向量化操作。
- 动态更改数据而无需类型统一 - 处理具有不同数据类型的 JSON 路径,而无需将它们统一为通用类型。
- 防止磁盘上出现大量列数据文件 - 避免为唯一的 JSON 路径在磁盘上创建过多的列文件。
- 密集存储 - 以密集、非冗余的方式存储唯一 JSON 路径的值。
目前这是一个实验性功能,因此您需要启用以下标志才能使用它
SET allow_experimental_json_type = 1;
完成后,您可以创建一个包含一个或多个 JSON
类型字段的表。我们将从 StatsBomb 开放数据集导入一个包含足球/足球赛事的 JSON 文件 (StatsBomb open dataset)。它只包含几千个事件,但这足以了解 JSON 类型的工作原理。
我们将创建以下表
CREATE TABLE events
(
matchId String,
json JSON,
possession_team_id String MATERIALIZED getSubcolumn(
json, 'possession_team.id')
)
ENGINE = MergeTree
ORDER BY possession_team_id;
然后导入数据
INSERT INTO events
SELECT
‘15946’ AS matchId,
json
FROM url(
'https://raw.githubusercontent.com/statsbomb/open-data/refs/heads/master/data/events/15946.json',
JSONAsObject
);
然后我们可以编写以下查询来查找最常见的事件类型
SELECT
json.type.name,
count()
FROM events
GROUP BY ALL
ORDER BY count() DESC
LIMIT 10;
┌─json.type.name─┬─count()─┐
│ Pass │ 1163 │
│ Ball Receipt* │ 1058 │
│ Carry │ 890 │
│ Pressure │ 212 │
│ Ball Recovery │ 89 │
│ Duel │ 53 │
│ Clearance │ 37 │
│ Goal Keeper │ 34 │
│ Block │ 32 │
│ Shot │ 28 │
└────────────────┴─────────┘
常见问题
让我们回顾一些关于在数据库中存储 JSON 的常见问题。
哪个数据库最适合 JSON?
这取决于您在 JSON 数据进入数据库后如何处理它!如果您正在检索单个文档,则像 MongoDB 或 Couchbase 这样的数据库可能是最佳选择。如果您要对许多 JSON 文档运行分析查询,则 ClickHouse 可能是更好的选择。
JSON 是 SQL 还是 NoSQL?
您可以将 JSON 数据存储在 NoSQL 和 SQL 数据库中。可以存储 JSON 的 NoSQL 数据库示例包括 MongoDB 和 Couchbase。可以存储 JSON 的 SQL 数据库示例包括 PostgreSQL 和 ClickHouse。
可以使用 SQL 查询 JSON 吗?
是的,ClickHouse 允许您使用 SQL 查询 JSON 数据。例如,假设我们有这样的 JSON 文档
{
"duration": 0,
"id": "9f6e2ecf-6685-45df-a62e-c2db3090f6c1",
"index": "1",
"minute": "0",
"period": "1",
"play_pattern": {"id": "1", "name": "Regular Play"},
"possession": "1",
"possession_team": {"id": "217", "name": "Barcelona"},
"second": "0",
"tactics": {"formation": "442", "lineup": []},
"team": {"id": "217", "name": "Barcelona" },
"timestamp": "00:00:00.000",
"type": {"id": "35", "name": "Starting XI"}
}
我们可以将此数据存储在名为 events
的表中,模式如下
┌─name───────────────┬─type───┐
│ matchId │ String │
│ json │ JSON │
│ possession_team_id │ String │
└────────────────────┴────────┘
然后我们可以编写以下查询来查找最受欢迎的事件类型
SELECT json.type.name, count() AS count
FROM events
GROUP BY ALL
ORDER BY count DESC
LIMIT 10;