在双活数据库中存储 JSON
双活数据库的 JSON 支持和冲突解决规则。
RedisJSON v2.2 在 双活 Redis Enterprise 数据库中增加了对 JSON 的支持。
该设计基于 Kleppmann 和 Beresford 的《无冲突复制 JSON 数据类型》(A Conflict-Free Replicated JSON Datatype),但实现包含一些更改。一些冲突解决规则示例也改编自这篇论文。
创建双活 JSON 数据库
要在双活数据库中使用 JSON,必须在创建数据库时启用 JSON。
双活 Redis Cloud 数据库默认添加 JSON。详情请参阅 Redis Cloud 文档中的创建双活数据库。
在 Redis Enterprise Software 中,双活数据库默认不启用 JSON。要在 Redis Enterprise Software 中创建双活 JSON 数据库
-
有关先决条件和详细步骤,请参阅 Redis Enterprise Software 文档中的创建双活地理复制数据库。
-
在创建双活数据库屏幕的“功能”部分,选择 JSON
注意选中 JSON 时,搜索和查询也会默认选中,以便您可以索引和查询 JSON 文档。如果您不想使用这些附加功能,可以取消选中搜索和查询复选框。 -
配置其他数据库设置。
-
选择创建。
命令差异
某些 JSON 命令在双活数据库中工作方式不同。
JSON.CLEAR
JSON.CLEAR
重置 JSON 数组和对象。它支持来自双活数据库不同实例对 JSON 文档的并发更新,并允许合并结果。
冲突解决规则
在双活数据库中,两个不同实例可能尝试同时对相同数据执行写操作。如果发生这种情况,当副本尝试相互同步这些更改时,可能会出现冲突。冲突解决规则决定了数据库如何处理冲突的操作。
冲突解决有两种类型
-
合并
-
操作是可结合的。
-
合并两个操作的结果。
-
-
胜者为准
-
操作是不可结合的。
-
其中一个操作赢得冲突并设置值。
-
忽略失败的操作。
-
以下冲突解决规则展示了双活数据库如何解决各种 JSON 命令的冲突。
将不同类型赋值给同一个键
冲突
两个实例并发地将不同类型的值赋给 JSON 文档中的同一个键。
例如
实例 1 将一个对象赋值给 JSON 文档中的一个键。
实例 2 将一个数组赋值给同一个键。
解决类型
胜者为准
解决规则
ID 较小的实例获胜,因此在给定示例中,键成为一个对象。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 将同一个键设置为对象或数组 | JSON.SET doc $.a '{}' | JSON.SET doc $.a '[]' |
t2 | 向对象和数组添加数据 | 结果 {"a": {"x": "y"}} |
结果 {“a”: ["z"]} |
t3 | 双活同步 | – 同步 – | – 同步 – |
t4 | 实例 1 获胜 | JSON.GET doc $ 结果 {"a": {"x": "y"}} |
JSON.GET doc $ 结果 {"a": {"x": "y"}} |
创建对比创建
冲突
两个实例并发地使用 JSON.SET
将新的 JSON 文档赋给同一个键。
解决类型
胜者为准
解决规则
ID 较小的实例获胜。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 创建新的 JSON 文档 | ||
t2 | 双活同步 | – 同步 – | – 同步 – |
t3 | 实例 1 获胜 | JSON.GET doc $ 结果 {"field": "a"} |
JSON.GET doc $ 结果 {"field": "a"} |
创建对比更新
冲突
实例 1 使用 JSON.SET
创建一个新文档并将其赋给一个现有键。
实例 2 使用 JSON.SET
更新同一个键的现有内容。
解决类型
胜者为准
解决规则
创建新文档的操作获胜。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 文档存在于两个实例上 | JSON.GET doc $ 结果 {"field1": "value1"} |
JSON.GET doc $ 结果 {"field1": "value1"} |
t2 | 实例 1 创建一个新文档;实例 2 更新现有文档 | ||
t3 | 双活同步 | – 同步 – | – 同步 – |
t4 | 实例 1 获胜 | JSON.GET doc . 结果 {"field2": "value2"} |
JSON.GET doc . 结果 {"field2": "value2"} |
删除对比创建
冲突
实例 1 使用 JSON.DEL
删除一个 JSON 文档。
实例 2 使用 JSON.SET
创建一个新的 JSON 文档并将其赋给实例 1 删除的键。
解决类型
胜者为准
解决规则
文档创建优先于删除。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 文档存在于两个实例上 | JSON.GET doc $ 结果 {"field1": "value1"} |
JSON.GET doc $ 结果 {"field1": "value1"} |
t2 | 实例 1 删除文档;实例 2 创建新文档 | JSON.DEL doc | |
t3 | 双活同步 | – 同步 – | – 同步 – |
t4 | 实例 2 获胜 | JSON.GET doc $ 结果 |
JSON.GET doc $ 结果 {"field1": "value2"} |
删除对比更新
冲突
实例 1 使用 JSON.DEL
删除一个 JSON 文档。
实例 2 使用 JSON.SET
更新同一文档的内容。
解决类型
胜者为准
解决规则
文档删除优先于更新。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 文档存在于两个实例上 | JSON.GET doc $ 结果 |
JSON.GET doc $ 结果 {"field1": "value1"} |
t2 | 实例 1 删除文档;实例 2 更新它 | JSON.DEL doc | |
t3 | 双活同步 | – 同步 – | – 同步 – |
t4 | 实例 1 获胜 | JSON.GET doc $ 结果 (nil) |
JSON.GET doc $ 结果 (nil) |
更新对比更新
冲突
实例 1 使用 JSON.SET
更新 JSON 文档中的一个字段。
实例 2 使用不同的值更新同一个字段。
解决类型
胜者为准
解决规则
ID 最小的实例获胜。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 文档存在于两个实例上 | JSON.GET doc $ 结果 {"field": "a"} |
JSON.GET doc $ 结果 {"field": "a"} |
t2 | 使用不同的数据更新同一个字段 | ||
t3 | 双活同步 | – 同步 – | – 同步 – |
t4 | 实例 1 获胜 | JSON.GET doc $ 结果 {"field": "b"} |
JSON.GET doc $ 结果 {"field": "b"} |
更新对比清除
v2.2 版本之前的 RedisJSON 有两种不同的方式来重置 JSON 对象的内容
-
赋值一个新的空 JSON 对象
JSON.SET doc $.colors '{}'
如果使用此方法,它无法与并发更新合并。
-
对于每个键,使用
JSON.DEL
将其移除JSON.DEL doc $.colors.blue
使用此方法,可以将重置与并发更新合并。
从 RedisJSON v2.2 开始,您可以使用 JSON.CLEAR
命令重置 JSON 文档,而无需手动移除每个键。此方法也允许合并并发更新。
赋值一个空对象
冲突
实例 1 使用 JSON.SET
将 "red" 添加到现有的 "colors" 对象中。
实例 2 为 "colors" 赋值一个新的空对象。
解决类型
胜者为准
解决规则
文档创建优先于更新,因此结果将是一个空对象。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 文档存在于两个实例上 | JSON.GET doc $ 结果 {"colors": {"blue": "#0000ff"}} |
JSON.GET doc $ 结果 {"colors": {"blue": "#0000ff"}} |
t2 | 实例 1 添加新颜色;实例 2 将 colors 重置为空对象 | JSON.SET doc $.colors ‘{}’ | |
t3 | 实例 2 添加新颜色 | ||
t4 | JSON.GET doc $ 结果 {"colors": {"blue": "#0000ff", "red": "#ff0000"}} |
JSON.GET doc $ 结果 {"colors": {"green": "#00ff00"}} |
|
t5 | 双活同步 | – 同步 – | – 同步 – |
t6 | 实例 2 获胜 | JSON.GET doc $ 结果 {"colors": {"green": "#00ff00"}} |
JSON.GET doc $ 结果 {"colors": {"green": "#00ff00"}} |
使用 JSON.CLEAR
冲突
实例 1 使用 JSON.SET
将 "red" 添加到现有的 "colors" 对象中。
实例 2 使用 JSON.CLEAR
清除 "colors" 对象,并将 "green" 添加到 "colors" 中。
解决类型
合并
解决规则
合并所有操作的结果。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 文档存在于两个实例上 | JSON.GET doc $ 结果 {"colors": {"blue": "#0000ff"}} |
JSON.GET doc $ 结果 {"colors": {"blue": "#0000ff"}} |
t2 | 实例 1 添加新颜色;实例 2 重置 colors | JSON.CLEAR doc $.colors | |
t3 | JSON.GET doc $ 结果 {"colors": {"blue": "#0000ff", "red": "#ff0000"}} |
JSON.GET doc $ 结果 {"colors": {}} |
|
t4 | 实例 2 添加新颜色 | ||
t5 | JSON.GET doc $ 结果 {"colors": {"green": "#00ff00"}} |
||
t6 | 双活同步 | – 同步 – | – 同步 – |
t7 | 合并两个实例的结果 | JSON.GET doc $ 结果 {"colors": {"red": "#ff0000", "green": "#00ff00"}} |
JSON.GET doc $ 结果 {"colors": {"red": "#ff0000", "green": "#00ff00"}} |
更新对比更新数组
冲突
两个实例使用不同的内容更新同一个现有数组。
解决类型
合并
解决规则
合并数组上所有操作的结果。保留来自每个实例的原始元素顺序。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 文档存在于两个实例上 | JSON.GET doc $ 结果 '["a", "b", "c"]' |
JSON.GET doc $ 结果 '["a", "b", "c"]' |
t2 | 实例 1 移除数组元素;实例 2 添加一个 | JSON.ARRPOP doc $ 1 结果 ["a", "c"] |
结果 ["y", "a", "b", "c"] |
t3 | 两个实例都向数组添加另一个元素 | 结果 ["a", "x", "c"] |
结果 ["y", "a", "z", "b", "c"] |
t4 | 双活同步 | – 同步 – | – 同步 – |
t5 | 合并两个实例的结果 | JSON.GET doc $ 结果 ["y", "a", "x", "z", "c"] |
JSON.GET doc $ 结果 ["y", "a", "x", "z", "c"] |
更新对比删除数组元素
冲突
实例 1 使用 JSON.ARRPOP
从 JSON 数组中移除一个元素。
实例 2 更新实例 1 移除的同一个元素。
解决类型
胜者为准
解决规则
删除优先于更新。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 文档存在于两个实例上 | JSON.GET doc $ 结果 {“todo”: [{“title”: “buy milk”, “done”: false}]} |
JSON.GET doc $ 结果 {“todo”: [{“title”: “buy milk”, “done”: false}]} |
t2 | 实例 1 移除数组元素;实例 2 更新同一个元素 | ||
t3 | JSON.GET doc $ 结果 {“todo”: []} |
JSON.GET doc $ 结果 [{“title”: “buy milk”, “done”: true}]} |
|
t4 | 双活同步 | – 同步 – | – 同步 – |
t5 | 实例 1 获胜 | JSON.GET doc $ 结果 doc = {“todo”: []} |
JSON.GET doc $ 结果 doc = {“todo”: []} |
更新对比更新对象
冲突
两个实例使用不同的内容更新同一个现有对象。
解决类型
合并
解决规则
合并对象上所有操作的结果。
示例
时间 | 描述 | 实例 1 | 实例 2 |
---|---|---|---|
t1 | 文档存在于两个实例上 | JSON.GET doc $ 结果 '{"grocery": []}' |
JSON.GET doc $ 结果 '{"grocery": []}' |
t2 | 向数组添加新元素 | JSON.ARRAPPEND doc $.grocery ‘“milk”’ | |
t3 | 向数组添加新元素 | JSON.ARRAPPEND doc $.grocery ‘“ham”’ | |
t4 | JSON.GET doc $ 结果 {"grocery":["eggs", "ham"]} |
JSON.GET doc $ 结果 {"grocery":["milk", "flour"]} |
|
t5 | 双活同步 | – 同步 – | – 同步 – |
t6 | 合并两个实例的结果 | JSON.GET doc . 结果 {"grocery":["eggs","ham","milk", "flour"]} |
JSON.GET doc . 结果 {"grocery":["eggs","ham","milk", "flour" ]} |