一个提供原生 JSON 功能的 Redis 模块——从GitHub 获取(https://github.com/RedisJSON/RedisJSON) 或在查阅在线文档(https://oss.redis.com/redisjson/)。
JSON 和 Redis 都不需要介绍,相信大家都很清楚他们。前者是现代应用程序交换数据的标准格式,而后者则是用在任何需要高性能数据管理的地方。比较令人震撼的是,这两个东西没法高效的结合在一起使用。(言外之意是,目前用redis存储 json 数据会比较消耗性能开销)。
Redis 又名“数据库的瑞士军刀”、“微服务的超级粘合剂” 等, 为特定任务提供了专门的工具。这里我们需要特别理解一点:Redis 可以开箱即用,也可以通过各种插件来达成我们的目的。开发人员使用这些插件作为抽象数据结构及其伴随操作公开的工具来为问题的最佳解决方案建模。之所以直接用 Redis 存储 Json 会不自然,是因为我们没有使用一个合适的插件。
事实上,尽管 Redis 有许多核心数据结构,但没有一个能符合JSON规范的要求。当然,我们也可以通过使用其他数据类型来解决这个问题:将 Json 序列化存储,或者对 Json 做hash。但是这些变通方案模式施加了限制,使其仅在少数用例中有用,即使这样,体验也会留下非 Redis 风格的回味。它们的笨拙与正常使用 Redis 的简单和优雅形成了鲜明的冲突。
但在去年(2020年) Salvatore Sanfilippo 访问 Tel Aviv 办公室后,这一切都发生了变化,Redis 模块化成为现实。这一点,直接提高了redis的上限。现在模块化可以让任何人做任何事情,在Dvir Volk 的指导下,诞生了 ReJSON。
该模块提供了一种新的数据类型,专为快速高效地操作 JSON 文档而设计。与任何 Redis 数据类型一样,ReJSON 的 value 存储在可以通过专门的命令访问的 key 中。这些命令或由模块公开的 API 旨在为从 JSON 世界来到 Redis 的用户提供直观的体验,反之亦然。考虑这个显示如何设置和获取值的示例:
127.0.0.1:6379> JSON.SET scalar . '"Hello JSON!"'
OK
127.0.0.1:6379> JSON.SET object . '{"foo": "bar", "ans": 42}'
OK
127.0.0.1:6379> JSON.GET object
"{\"foo\":\"bar",\"ans\":42}"
127.0.0.1:6379> JSON.GET object .ans
"42"
127.0.0.1:6379> ^C
~$
像任何表现良好的模块一样,ReJSON 的命令带有前缀。JSON.SET与JSON.GET 这两个方法都期望键的名称作为其第一个参数。在第一行中,我们将key 为 scalar 对应 value(由句点字符:“.”表示)设置为字符串。接下来,使用 JSON 对象(首先读取整个对象)设置另一个key 名为 object,然后输入json 字符串。
这背后的原理是,无论何时调用JSON.SET,该模块都会通过流式词法分析器获取值,该词法分析器解析输入 JSON 并从中构建树形数据结构:
ReJSON 以二进制格式将数据存储在树的节点中,并支持JSONPath的子集,以便于引用子元素。它拥有为每种 JSON 值类型量身定制的原子命令库,包括:JSON.STRAPPEND用于附加字符串;JSON.NUMMULTBY用于乘法;并JSON.ARRTRIM用于修剪阵列 等等。
因为 ReJSON 是作为 Redis 模块实现的,所以您可以将它与任何 Redis 客户端一起使用:a) 支持模块(无 ATM)
b) 允许发送原始命令(大多数是 ATM)
例如,您可以在 Python 代码中通过redis-py使用启用 ReJSON 的 Redis 服务器,如下所示:
import redis
import json
data = {
'foo': 'bar',
'ans': 42
}
r = redis.StrictRedis()
r.execute_command('JSON.SET', 'object', '.', json.dumps(data))
reply = json.loads(r.execute_command('JSON.GET', 'object'))
但这只是其中的一半。ReJSON 不仅是一个漂亮的 API——它在性能方面也是一个强大的动力。初始性能基准已经证明,例如:
上图比较了 3.4KB JSON (三层嵌套级别) 在执行的读取和写入操作的速率(操作/秒)和平均延迟,这里将 ReJSON 与字符串存储json的两种变体进行了对比。两种变体都实现为 Redis 服务器端 Lua 脚本,其中 json.lua 变体存储原始序列化 JSON,msgpack.lua 使用 MessagePack 编码。可以看出,如果不取 JSON 元素的值,Get 或者 Set 整个json 字符串,ReJson 的效果不如另外两种使用方法。但是一旦你对 json 中某个元素取值,或者做set 操作,会发现 Rejson 的性能甚至有所提高(内部使用了树索引,所以会更快一些)
以上这些,你今天就可以开始玩 ReJSON 了!