在 Active-Active 数据库中存储 JSON
Active-Active 数据库的 JSON 支持和冲突解决规则。
RedisJSON v2.2 为 Active-Active Redis Enterprise 数据库 添加了对 JSON 的支持。
该设计基于 Kleppmann 和 Beresford 的 无冲突复制 JSON 数据类型,但实现中包含一些更改。此外,还从这篇论文中改编了多个 冲突解决规则 示例。
创建活动-活动 JSON 数据库
要在活动-活动数据库中使用 JSON,您必须在创建数据库期间启用 JSON。
活动-活动 Redis Cloud 数据库默认情况下添加 JSON。有关详细信息,请参阅 Redis Cloud 文档中的 创建活动-活动数据库。
在 Redis Enterprise 软件中,JSON 默认情况下未为活动-活动数据库启用。要在 Redis Enterprise 软件中创建活动-活动 JSON 数据库,
-
请参阅 Redis Enterprise 软件文档中的 创建活动-活动地理复制数据库,了解先决条件和详细步骤。
-
在“创建活动-活动数据库”屏幕的“功能”部分中,选择“JSON”。
注意当您选择“JSON”时,“搜索和查询”也会默认选中,以允许您索引和查询 JSON 文档。如果您不想使用这些附加功能,可以取消选中“搜索和查询”复选框。 -
配置其他数据库设置。
-
选择“创建”。
命令差异
某些 JSON 命令在活动-活动数据库中工作方式不同。
JSON.CLEAR
JSON.CLEAR
重置 JSON 数组和对象。它支持活动-活动数据库中不同实例对 JSON 文档的并发更新,并允许合并结果。
冲突解决规则
使用活动-活动数据库,两个不同的实例有可能同时尝试对同一数据运行写入操作。如果发生这种情况,当副本尝试彼此同步这些更改时,可能会出现冲突。冲突解决规则决定数据库如何处理冲突操作。
有两种类型的冲突解决
-
合并
-
操作是关联的。
-
合并两个操作的结果。
-
-
胜出
-
操作不是关联的。
-
一个操作在冲突中获胜并设置值。
-
忽略失败的操作。
-
以下冲突解决规则显示了活动-活动数据库如何为各种 JSON 命令解决冲突。
为键分配不同的类型
冲突
两个实例同时将不同类型的 value 分配给 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 将颜色重置为一个空对象 | 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 重置颜色 | 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" ]} |