在 Valkey 中,JSON 数据类型和命令在 valkey-json 模块中实现,这是一个兼容 Valkey 8.0 及更高版本的官方 Valkey 模块。用户需要将此模块加载到其 Valkey 服务器上才能使用此功能。
Valkey JSON 是一个用 C++ 编写的 Valkey 模块,为 Valkey 提供原生的 JSON (JavaScript Object Notation) 支持。该实现符合 RFC7159 和 ECMA-404 JSON 数据交换标准。用户可以使用 JSONPath 查询语言原生存储、查询和修改 JSON 数据结构。查询表达式支持高级功能,包括通配符选择、过滤表达式、数组切片、联合操作和递归搜索。
Valkey JSON 命令示例
JSON.SET
在指定路径设置 JSON 值。JSON.GET
获取一个或多个路径上的序列化 JSON。JSON.ARRINSERT
在指定路径的数组值中,在指定索引前插入一个或多个值。JSON.ARRLEN
获取指定路径下数组的长度。
Valkey JSON 的常见用例
在 valkey-json 模块出现之前,在 Valkey 中存储 JSON 通常涉及将其序列化为字符串或使用哈希数据类型。这种方法使得处理嵌套数据或执行局部更新变得困难。valkey-json 通过启用原生 JSON 支持解决了这些限制。
Valkey JSON 提供了一种高效存储和操作结构化数据的方法。其主要优点包括快速搜索和过滤功能。它还提供了对 JSON 数据执行就地更新的能力,而无需覆盖整个文档。这些功能使您能够高效地查询、修改和管理复杂的数据结构,使其成为需要动态和灵活数据存储的应用程序的理想选择。
JSON 属性
-
最大文档大小 - valkey-json 允许配置单个 JSON 键的大小限制,以防止恶意或无限制插入(例如,通过
JSON.ARRINSERT
)可能导致的内存不足问题。默认情况下,此值为 0,表示没有限制。您可以使用CONFIG SET json.max-document-size <value>
命令设置此限制。使用JSON.DEBUG MEMORY <key>
或MEMORY USAGE <key>
检查内存使用情况。 -
最大深度 - JSON 对象和数组的最大嵌套级别。如果一个 JSON 对象或数组包含另一个对象或数组,则视为嵌套。默认允许的最大嵌套深度为 128。任何尝试超出此限制的操作都将导致错误。您可以使用以下命令调整此限制:
CONFIG SET json.max-path-limit <value>
,其中 value 是所需的深度限制。
性能
Valkey 中 JSON 操作的性能主要受 JSONPath 评估的复杂性以及 JSON 对象的大小或深度的影响。大多数操作(包括 JSON.GET
、JSON.SET
、JSON.DEL
、JSON.NUMINCRBY
、JSON.STRAPPEND
和 JSON.ARRAPPEND
)在使用直接路径时通常是 O(1) 的复杂度,但当匹配的路径数量为 N 时,复杂度可能会增加到 O(N)。像 $.user.name
这样更简单的路径比诸如 $..[?(@.active==true)]
等递归或过滤查询的执行速度显著更快。
为了获得最佳性能,建议尽量减少过度嵌套,避免频繁修改深层嵌套对象,并谨慎使用路径过滤器。
JSON ACL
-
与现有的按数据类型分类(@string、@hash 等)类似,新增了一个 @json 类别,以简化对 JSON 命令和数据的访问管理。没有其他现有 Valkey 命令属于 @json 类别。所有 JSON 命令都强制执行任何键空间或命令限制和权限。
-
有 4 个现有的 Valkey ACL 类别已更新以包含新的 JSON 命令:@read、@write、@fast、@slow。下表显示了 JSON 命令与相应类别的映射关系。
对于每个类别和每个命令的行,如果单元格包含“y”,则表示该命令已添加到该类别中。
JSON 命令 | @json | @read | @write | @fast | @slow |
---|---|---|---|---|---|
JSON.ARRAPPEND | y | y | y | ||
JSON.ARRINDEX | y | y | y | ||
JSON.ARRINSERT | y | y | y | ||
JSON.ARRLEN | y | y | y | ||
JSON.ARRPOP | y | y | y | ||
JSON.ARRTRIM | y | y | y | ||
JSON.CLEAR | y | y | y | ||
JSON.DEBUG | y | y | y | ||
JSON.DEL | y | y | y | ||
JSON.FORGET | y | y | y | ||
JSON.GET | y | y | y | ||
JSON.MGET | y | y | y | ||
JSON.MSET | y | y | y | ||
JSON.NUMINCRBY | y | y | y | ||
JSON.NUMMULTBY | y | y | y | ||
JSON.OBJKEYS | y | y | y | ||
JSON.OBJLEN | y | y | y | ||
JSON.RESP | y | y | y | ||
JSON.SET | y | y | y | ||
JSON.STRAPPEND | y | y | y | ||
JSON.STRLEN | y | y | y | ||
JSON.TOGGLE | y | y | y | ||
JSON.TYPE | y | y | y |
路径语法
Valkey JSON 支持两种路径语法:
- 增强语法 – 遵循 Goessner 描述的 JSONPath 语法,如下表所示。我们对表中的描述进行了重新排序和修改以提高清晰度。
- 受限语法 – 查询功能有限。
如果查询路径以 $
开头,则使用增强语法。否则,使用受限语法。建议您在新开发中使用增强语法。
增强语法符号与表达式
符号/表达式 | 描述 |
---|---|
$ | 根元素。 |
. 或 [] | 子操作符。 |
.. | 递归下降。 |
* | 通配符。对象或数组中的所有元素。 |
[] | 数组下标操作符。索引从 0 开始。 |
[ , ] | 联合操作符。 |
[start:end:step] | 数组切片操作符。 |
?() | 将过滤器(脚本)表达式应用于当前数组或对象。 |
() | 过滤表达式。 |
@ | 用于引用正在处理的当前节点的过滤表达式。 |
== | 等于,用于过滤表达式。 |
!= | 不等于,用于过滤表达式。 |
> | 大于,用于过滤表达式。 |
>= | 大于或等于,用于过滤表达式。 |
< | 小于,用于过滤表达式。 |
<= | 小于或等于,用于过滤表达式。 |
&& | 逻辑与,用于组合多个过滤表达式。 |
|| | 逻辑或,用于组合多个过滤表达式。 |
示例
以下示例基于 Goessner 的示例 XML 数据构建,我们通过添加额外字段对其进行了修改。
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95,
"in-stock": true,
"sold": true
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99,
"in-stock": false,
"sold": true
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99,
"in-stock": true,
"sold": false
},
{ "category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99,
"in-stock": false,
"sold": false
}
],
"bicycle": {
"color": "red",
"price": 19.95,
"in-stock": true,
"sold": false
}
}
}
路径 | 描述 |
---|---|
'$.store.book[*].author' | 商店中所有图书的作者。 |
'$..author' | 所有作者。 |
'$.store.*' | 商店的所有成员。 |
'$["store"].*' | 商店的所有成员。 |
'$.store..price' | 商店中所有商品的价格。 |
'$..*' | JSON 结构的所有递归成员。 |
'$..book[*]' | 所有书籍。 |
'$..book[0]' | 第一本书。 |
'$..book[-1]' | 最后一本书。 |
'$..book[0:2]' | 前两本书。 |
'$..book[0,1]' | 前两本书。 |
'$..book[0:4]' | 索引从 0 到 3 的书籍(不包含结束索引)。 |
'$..book[0:4:2]' | 索引 0 和 2 的书籍。 |
'$..book[?(@.isbn)]' | 所有带有 ISBN 号的书籍。 |
'$..book[?(@.price < 10)]' | 所有价格低于 $10 的书籍。 |
'$..book[?(@["price"] < 10)]' | 所有价格低于 $10 的书籍。(替代语法) |
'$..book[?(@.["price"] < 10)]' | 所有价格低于 $10 的书籍。(替代语法) |
'$..book[?(@.price >= 10 && @.price <= 100)]' | 所有价格在 $10 到 $100 之间(含两端)的书籍。 |
'$..book[?(@.sold == true || @.in-stock == false)]' | 所有已售出或缺货的书籍。 |
'$.store.book[?(@.["category"] == "fiction")]' | 所有小说类书籍。 |
'$.store.book[?(@.["category"] != "fiction")]' | 所有非小说类书籍。 |
更多过滤表达式示例
127.0.0.1:6379> JSON.SET k1 . '{"books": [{"price":5,"sold":true,"in-stock":true,"title":"foo"}, {"price":15,"sold":false,"title":"abc"}]]: '
OK
127.0.0.1:6379> JSON.GET k1 $.books[?(@.price>1&&@.price<20&&@.in-stock)]
"[{\"price\":5,\"sold\":true,\"in-stock\":true,\"title\":\"foo\"]: ]"
127.0.0.1:6379> JSON.GET k1 '$.books[?(@.price>1 && @.price<20 && @.in-stock)]'
"[{\"price\":5,\"sold\":true,\"in-stock\":true,\"title\":\"foo\"]: ]"
127.0.0.1:6379> JSON.GET k1 '$.books[?((@.price>1 && @.price<20) && (@.sold==false))]'
"[{\"price\":15,\"sold\":false,\"title\":\"abc\"]: ]"
127.0.0.1:6379> JSON.GET k1 '$.books[?(@.title == "abc")]'
[{"price":15,"sold":false,"title":"abc"]: ]
127.0.0.1:6379> JSON.SET k2 . '[1,2,3,4,5]'
127.0.0.1:6379> JSON.GET k2 $.*.[?(@>2)]
"[3,4,5]"
127.0.0.1:6379> JSON.GET k2 '$.*.[?(@ > 2)]'
"[3,4,5]"
127.0.0.1:6379> JSON.SET k3 . '[true,false,true,false,null,1,2,3,4]'
OK
127.0.0.1:6379> JSON.GET k3 $.*.[?(@==true)]
"[true,true]"
127.0.0.1:6379> JSON.GET k3 '$.*.[?(@ == true)]'
"[true,true]"
127.0.0.1:6379> JSON.GET k3 $.*.[?(@>1)]
"[2,3,4]"
127.0.0.1:6379> JSON.GET k3 '$.*.[?(@ > 1)]'
"[2,3,4]"
受限语法符号与表达式
符号/表达式 | 描述 |
---|---|
. 或 [] | 子操作符。 |
[] | 数组下标操作符。索引从 0 开始。 |
路径 | 描述 |
---|---|
'.store.book[0].author' | 第一本书的作者。 |
'.store.book[-1].author' | 最后一本书的作者。 |
'.address.city' | 城市名称。 |
'["store"]["book"][0]["title"]' | 第一本书的标题。 |
'["store"]["book"][-1]["title"]' | 最后一本书的标题。 |
常见错误前缀
每个错误消息都有一个前缀。以下是常见错误前缀的列表。
前缀 | 描述 |
---|---|
ERR | 一般错误。 |
LIMIT | 当超出大小限制时发生的错误。例如,文档大小限制或嵌套深度限制被超出。 |
NONEXISTENT | 键或路径不存在。 |
OUTOFBOUNDARIES | 数组索引超出范围。 |
SYNTAXERR | 语法错误。 |
WRONGTYPE | 值类型错误。 |
JSON 相关指标
信息 | 描述 |
---|---|
json_total_memory_bytes | 分配给 JSON 对象的总内存。 |
json_num_documents | Valkey 中的文档总数 |
要查询核心指标,请运行以下命令:info json_core_metrics