dot 速度的未来将在您所在的城市举办的活动中揭晓。

加入我们参加 Redis 发布会

使用 Redis 对 JSON 文档进行索引、查询和全文搜索

相关资源:点击 下载 RedisJSON 模块

RedisJSON 和 RediSearch 是我们云中最受欢迎的 Redis 模块。(参见图 1)RedisJSONRediSearch(与 Redis 捆绑在一起)的 Docker 镜像每天被拉取 超过 2000 次。这就是为什么我们认为 Redis 的技术布道者 Itamar Haber 在 4 年前编写第一个版本时是一个有远见的人。在 4 月份,我们在 RedisConf 上宣布了与 JSON、索引和全文搜索功能相关的几项 公告。今天,我们很高兴宣布这些功能的私人预览。

在这篇博文中,我们将概述当前 RedisJSON 的功能。之后,我们将深入探讨此私人预览的新功能部分。能够使用 RediSearch 对 JSON 文档进行索引、查询和使用全文搜索是此版本最酷的新功能。最后,我们将向您展示如何快速上手。

modules by REC
图 1. 使用至少一个模块的 Redis 云数据库(2021 年 5 月)

JSON 功能

当您没有 RedisJSON 时,您可以通过使用字符串数据结构在 Redis 中对嵌套文档进行建模。  

redis.cloud:6379> SET myDoc '{"colors": ["green"]}'
OK

但是,如果我们需要更新文档的某个子部分呢?

为了保持操作的原子性,我们需要

  1. 监视 文档
  2. 读取先前版本并将其反序列化
  3. 将更新嵌入 Redis 事务中
  4. 序列化为 JSON 并更新文档
  5. 执行事务

如果另一个客户端在此过程中更新了文档,我们可能需要重试所有这些步骤。

redis.cloud:6379> WATCH myDoc
OK
redis.cloud:6379> GET myDoc
"{\"colors\": \"green\"}"
redis.cloud:6379> MULTI
OK
redis.cloud:6379>(TX) SET myDoc '{"colors": ["green", "blue"]}'
QUEUED
redis.cloud:6379>(TX) EXEC
1) OK

但是,使用 RedisJSON,我们可以 **通过一个原子事务** 来完成此更新

redis.cloud:6379> JSON.ARRAPPEND myDoc colors '"blue"'
(integer) 2

让我们看另一个示例,在这个示例中,您有一个大型 JSON,但在应用程序中只需要该文档的某个子部分。

没有 RedisJSON

您必须

  1. 检索整个 json 字符串,将其序列化为字符串
  2. 反序列化 JSON
  3. 提取您需要的子部分
client.get("myDoc", function(err, reply) {
  const myJson = JSON.parse(myJsonString);
  const color = myJson.colors[0];
});

使用 RedisJSON,您可以使用单个命令检索您需要的数据,从而最大限度地减少 CPU 周期、网络开销,最重要的是延迟。

redis.cloud:6379> JSON.GET myDoc $.colors[0]
"\"green\""

如您所见,RedisJSON 简化了 JSON 文档操作。RedisJSON 的当前 GA 版本(v1.0)是社区已广泛使用并解决使用字符串数据结构对嵌套结构进行建模的缺点的版本。以下是其一些主要功能的概述。

存储(或更新)与 Redis 中的键关联的 JSON 文档

redis.cloud:6379> JSON.SET myDoc . '{"title": "css", "colors": ["green"]}'
OK

替换子部分(例如,键的字符串值)

redis.cloud:6379> JSON.SET myDoc title '"style"'
OK

向集合或映射添加项目

redis.cloud:6379> JSON.ARRAPPEND myDoc colors '"red"' '"blue"'
(integer) 3

提取整个文档

redis.cloud:6379> JSON.GET myDoc .
"{\"title\":\"css\",\"colors\":[\"green\"]}"

使用 JSONPath 子集提取文档的一部分

redis.cloud:6379> JSON.GET myDoc colors[0]
"\"green\""

RedisJSON 2.0:私人预览版

我们在 RedisConf 2021 上宣布了此版本,今天,我们很高兴宣布它已作为私人预览版提供给我们的部分 Redis Enterprise 客户,并且作为候选版本提供给我们的社区。此版本具有三个主要功能,即完全支持 JSONPath 表达式、支持主动-主动(使用 Redis Enterprise)以及能够使用 RediSearch 对 JSON 文档进行索引、查询和使用全文搜索。但还有更多!让我们深入了解这些新功能。

用 RUST 重写

系统编程语言 是一组面向效率的语言。用这些语言编写的程序通常很轻量级,并提供最佳性能。这就是 Redis 历来是用 C 语言编写的原因。这也解释了为什么 Redis 能够实现极低的延迟和高吞吐量。大多数 Redis 模块都是用 C、C++ 或 Rust 语言编写的,这些语言属于同一个系列。

JSON 特别受 Rust 社区的青睐,包括非常快速高效的 JSON 序列化JSONPath 实现。将这些实现的好处带给 Redis 用户是显而易见的,只需要在 Redis 模块 API 和 Rust 之间进行映射即可。 

完全支持 JSONPath

这就是 Rust 重写的优势所在。此新版本包含对 JSONPath 的全面支持。现在可以使用 JSONPath 表达式的全部表达能力。

给定一个 JSON 文档

redis.cloud:6379> JSON.SET myDoc $ '{"colors":["red", "blue", "green"]}'
OK

通配符(以前仅限于第一个项目)

redis.cloud:6379> JSON.GET myDoc $.colors[*]
"[\"red\",\"blue\",\"green\"]"

提取切片

redis.cloud:6379> JSON.GET myDoc $.colors[0:2]
"[\"red\",\"blue\"]"
redis.cloud:6379> JSON.GET myDoc $.colors[-1]
"["\"green\"]"

带有筛选表达式的更高级示例

redis.cloud:6379> JSON.SET myDoc $ '{"books": [{"title": "Peter Pan", "price": 8.95}, {"title": "Moby Dick", "price": 12.99}]}'

redis.cloud:6379> JSON.GET myDoc '$.books[?(@.price < 10)]'
"[{\"title\":\"Peter Pan\",\"price\":8.95}]"

支持主动-主动

主动-主动 是 Redis Enterprise 提供的功能。主动-主动允许您将数据库复制到多个地理分布式 Redis Enterprise 集群中。用户可以连接到具有本地读写延迟的最近集群。

该实现基于 无冲突复制数据类型 (CRDT) 技术。在为 Redis 支持的大多数核心数据结构实现它时,Redis 积累了丰富的知识和经验,而这种新的针对 JSON 的实现证实了这一点。

应用程序开发人员现在可以依赖此功能来构建使用 JSON 文档的地理分布式应用程序。以下是在具有两个集群的主动-主动环境中一系列操作的示例

Clusters

让我们看看每个操作的详细信息

  • T1:客户端在集群 1 上设置 JSON 文档。
  • T2:同步过程将文档复制到集群 2。
  • T3:两个集群都包含相同的文档。
  • T4:客户端在集群 1 中的 colors 数组中添加蓝色,同时,另一个客户端在集群 2 中的同一个数组中添加绿色。
  • T5:同步过程合并操作并更新两个集群上的文档。
  • T6:两个集群都包含相同的文档。

RediSearch 2.2:私人预览版

这篇博文还宣布了 RediSearch 2.2 的私人预览版可用(作为私人预览版提供给我们的部分 Redis Enterprise 客户,以及作为候选版本提供给我们的社区)。

在本节中,我们将描述此新版本的 RediSearch 提供的新功能。但首先,以下是我们同时发布这两个流行模块的原因

JSON 文档的索引、查询和全文搜索

这个特别的新功能将使 Redis 的 JSON 功能提升到一个全新的水平。RediSearch 一直在提供对哈希的索引和搜索功能,而不仅仅是一个键值存储。在幕后,RedisJSON 2.0 公开了内部公共 API。内部,因为此 API 公开给在 Redis 节点内运行的其他模块。公共,因为任何模块都可以使用此 API。RediSearch 2.2 也是如此!

通过将其功能公开给其他模块,RedisJSON 使 RediSearch 能够对 JSON 文档进行索引,以便用户现在可以通过索引和查询内容来查找文档。这些组合模块为您提供了 **功能强大、低延迟、面向 JSON 的文档数据库**!

让我们看看它将是什么样子。

我们应该首先使用 JSON.SET 命令用 JSON 文档填充数据库。

redis.cloud:6379> JSON.SET myDoc $ '{"title": "foo", "content": "bar"}'
OK

要创建新索引,我们使用 FT.CREATE 命令。索引的模式现在接受 JSONPath 表达式。表达式的结果被索引并与一个属性关联(此处:title)。

redis.cloud:6379> FT.CREATE myIdx ON JSON SCHEMA $.title AS title TEXT
OK

我们现在可以执行搜索查询并使用 FT.SEARCH 查找我们的 JSON 文档

redis.cloud:6379> FT.SEARCH myIdx "@title:foo"
1) (integer) 1
2) "myDoc"
3) 1) "$"
   2) "{\"title\":\"foo\",\"content\":\"bar\"}"

对 JSON 文档进行聚合

聚合 是 RediSearch 的一项强大功能,可用于创建分析报告或执行分面搜索样式的查询。现在 RediSearch 可以访问 JSON 文档,因此可以使用 JSONPath 表达式加载 JSON 文档中的任何值,并在管道中使用它,无论该值是否被索引。

让我们创建一个索引

redis.cloud:6379> FT.CREATE myIdx ON JSON SCHEMA $.user.name AS name TEXT
OK

向数据库添加 JSON 文档

redis.cloud:6379> JSON.SET myDoc . '{"user":{"name":"John 
Smith","hp":1000, "dmg":150}}'
OK

并使用从 JSON 文档中提取的两个数值执行简单的计算

redis.cloud:6379> FT.AGGREGATE myIdx '*' LOAD 6 $.user.hp AS hp $.user.dmg AS dmg APPLY '@hp - @dmg' AS points
1) (integer) 1
2) 1) "point"
   2) "850"

索引策略更加灵活

使用新版本的 RediSearch,现在可以使用不同的参数对同一个值(哈希字段或 JSON 文档中的 JSON 值)进行索引。这是一个典型用例,可以通过此新功能解决

让我们有一个包含属于类别的文档的数据库。

redis.cloud:6379> HSET myDoc category "foo,bar,hello world"
(integer) 1

使用 TAG 类型,您可以轻松地根据任何类别筛选搜索结果

redis.cloud:6379> FT.CREATE myIdx ON HASH SCHEMA category TAG
OK
redis.cloud:6379> FT.SEARCH myIdx "@category:{foo}"
1) (integer) 1
2) "myDoc"
3) 1) "category"
   2) "foo,bar,hello world"

但如果您还想对类别执行全文搜索呢?

redis.cloud:6379> FT.SEARCH myIdx "@category:{foo} @category:(hello)"
1) (integer) 0

到目前为止,对于哈希,您必须将值复制到两个字段中,这会消耗两倍的内存。

这就是 FT.CREATE…AS 变得非常方便的地方。让我们回到我们简洁明了的文档

redis.cloud:6379> HSET myDoc category "foo,bar,hello world"
(integer) 1

…并使用新的 AS 功能

redis.cloud:6379> FT.CREATE myIdx ON HASH SCHEMA category TAG category 
AS cat_txt TEXT
OK

…然后…

redis.cloud:6379> FT.SEARCH myIdx "@category:{foo} @cat_txt:(hello)"
1) (integer) 1
2) "myDoc"
3) 1) "category"
   2) "foo,bar,hello world"

太棒了!我们现在可以按标签进行筛选,并在同一个字段中进行全文搜索,**而无需复制数据。**

查询分析

大多数 Redis 命令的时间复杂度都有很好的记录。例如,HMGET 的复杂度为 O(N),其中“N 是请求的字段数”。使用 RediSearch,您可以编写高级查询。但是,FT.SEARCH 和 FT.AGGREGATE 命令的复杂度取决于查询的复杂度。

我们希望为您提供工具,帮助您了解执行查询时后台发生的情况,找出时间消耗的位置以及如何优化查询。新的 FT.PROFILE 命令返回一个树,显示 RediSearch 用于执行查询的主要步骤。对于每个步骤,都会提供时间信息。

那么,当我们使用模糊搜索执行查询时,RediSearch 内部会发生什么呢?

让我们看一个例子

redis.cloud:6379> HSET doc:1 text "hello world"
(integer) 1

redis.cloud:6379> HSET doc:2 text "hallo world" 
(integer) 1

redis.cloud:6379> FT.CREATE idx ON HASH SCHEMA text TEXT
OK

我们已准备好分析我们的查询。让我们运行分析并分解分析结果。

redis.cloud:6379> FT.PROFILE idx SEARCH LIMITED QUERY "%hello%"

首先,我们得到结果。这对于检查分析查询是否返回预期结果非常有用。

1) 1) (integer) 2
   2) "doc:2"
   3) 1) "text"
      2) "hallo world"
   4) "doc:1"
   5) 1) "text"
      2) "hello world"

这是总时间,称为“分析时间”,因为它包括在收集分析信息中花费的时间。

2) 1) 1) Total profile time
      2) "1.552"

解析查询和构建执行计划所花费的时间。

2) 1) Parsing time
      2) "0.90900000000000003"
   3) 1) Pipeline creation time
      2) "0.105"

这是在字典中查找模糊匹配所花费的时间。

4) 1) Iterators profile
      2)  1) Type
          2) UNION
          3) Query type
          4) "FUZZY - hello"
          5) Time
          6) "0.025999999999999999"
          7) Counter
          8) (integer) 2
          9) Child iterators
         10) "The number of iterators in the union is 2"

最后,您是否想知道构建搜索结果意味着什么?我们需要计算每个文档的全文得分,按得分对其进行排序,最后加载字段。有了这些信息,您可以识别瓶颈,使查询速度更快,并提高服务器的性能。

5) 1) Result processors profile
      2) 1) Type
         2) Index
         3) Time
         4) "0.040000000000000001"
         5) Counter
         6) (integer) 2
      3) 1) Type
         2) Scorer
         3) Time
         4) "0.026000000000000002"
         5) Counter
         6) (integer) 2
      4) 1) Type
         2) Sorter
         3) Time
         4) "0.032000000000000001"
         5) Counter
         6) (integer) 2
      5) 1) Type
         2) Loader
         3) Time
         4) "0.255"
         5) Counter
         6) (integer) 2

如何开始

我们相信这些新功能将改变应用程序开发人员和 Redis 社区的格局。以下是如何开始。

使用预览版本的 Docker 镜像

要开始,您可以使用以下 Docker 镜像,带有 :preview 标签

docker run -p 6379:6379 redis/redismod:preview

或者,您可以从 RC1 版本标签(v2.2.0 用于 RediSearch,v2.0.0 用于 RedisJSON)开始编译,并在两个存储库中都加载它们到 Redis。

一旦您启动并运行,您可以尝试所有上述命令,也可以使用此 快速入门指南。我们还将发布一系列关于 *RedisMart* 的博客,这是一个我们曾在 RedisConf 2021 主题演讲中展示 的在线零售应用程序。RedisMart 利用以地理分布方式部署的 RediSearch 和 RedisJSON 来提供 最佳在线零售体验。在本系列中,我们将逐步引导您了解如何构建此应用程序。

使用最新版本的兼容客户端进行开发

以下客户端列表当前正在升级,因此您可以使用这些新功能,并获得良好的开发体验。检查最新版本和/或拉取请求(目前,大多数客户端在主分支上支持预览版本)。

RedisJSONRediSearch
Node.jsredis-modules-sdkredis-modules-sdk
JavaJredisJSONJRediSearch
.NETNRedisJSONNRediSearch
Pythonredisjson-pyredisearch-py

加入社区

在我们努力实现通用可用性期间,我们欢迎您提供任何反馈意见、错误报告和功能请求。在文档网站或 RediSearch(在 Github 上)或 RedisJSON(在 Github 上)的 Github 存储库中留下反馈意见,或在 Discord 上与我们联系。