使用Active-Active数据库开发应用程序
概述了为Active-Active数据库开发应用程序与独立Redis数据库开发应用程序的不同之处。
开发地理分布的多主应用程序可能会很困难。应用程序开发人员可能需要理解大量竞争条件,这些条件涉及对各个站点的更新、网络和集群故障,这些故障可能会重新排序事件并改变跨地理分布写入的更新结果。
Active-Active数据库(以前称为CRDB)是跨多个Redis Enterprise Software(RS)集群的地理分布式数据库。 Active-Active数据库依赖于多主复制(MMR)和无冲突复制数据类型(CRDTs),为地理分布式应用程序提供简单的开发体验。 Active-Active数据库允许开发人员使用现有的Redis数据类型和命令,但理解开发人员的意图,并自动处理跨多个地理位置的相同键的并发写入冲突。 例如,开发人员可以在地理分布式应用程序的所有实例中简单地使用Redis中的INCR或INCRBY方法,Active-Active数据库会处理INCR的加法性质以反映正确的最终值。 以下示例显示了一系列事件随时间的变化:t1到t9。这个Active-Active数据库有两个成员Active-Active数据库:成员CRDB1和成员CRDB2。 在每个成员Active-Active数据库中执行的本地操作列在成员Active-Active数据库名称下。"Sync"事件表示同步赶上以将所有本地成员Active-Active数据库更新分发到其他参与集群和其他成员Active-Active数据库的时刻。
时间 | 成员 CRDB1 | 成员 CRDB2 |
---|---|---|
t1 | INCRBY key1 7 | |
t2 | INCRBY key1 3 | |
t3 | 获取 key1 7 |
获取 key1 3 |
t4 | — 同步 — | — 同步 — |
t5 | GET key1 10 |
GET key1 10 |
t6 | DECRBY key1 3 | |
t7 | INCRBY key1 6 | |
t8 | — 同步 — | — 同步 — |
t9 | GET key1 13 |
GET key1 13 |
数据库提供了多种方法来解决其中一些问题:
- 主动-被动地理分布式部署:在主动-被动分布中,所有写入都发送到主动集群。Redis Enterprise 提供了“副本”功能,提供了类似的方法。当工作负载严重偏向读取而写入较少时,可以采用这种方法。然而,WAN 性能和可用性相当不稳定,长距离写入会降低应用程序的性能和可用性。
- 两阶段提交(2PC):这种方法围绕一个协议设计,该协议在多个事务管理器之间提交事务。两阶段提交提供了跨区域的一致事务写入,但除非所有参与的事务管理器在事务发生时都“可用”,否则事务将失败。交换的消息数量及其跨区域可用性要求使得两阶段提交甚至不适合中等吞吐量和跨地理区域的写入,这些写入需要通过广域网(WAN)进行。
- 基于Quorum的同步更新写入:这种方法在跨多个区域的集群中同步协调大多数副本的写入。然而,就像两阶段提交一样,交换的消息数量及其跨区域可用性要求使得地理分布的Quorum写入不适合中等吞吐量和跨地理区域的WAN写入。
- 最后写入者胜(LWW)冲突解决:某些系统为所有类型的写入提供简化的冲突解决机制,其中使用系统时钟来确定冲突写入中的胜者。LWW 是轻量级的,可能适用于较简单的数据。然而,LWW 可能会破坏不一定冲突的更新。例如,在两个地理位置同时向集合中添加新元素,使用 LWW 将导致最终结果中只出现其中一个新元素。
- MVCC(多版本并发控制):MVCC系统维护数据的多个版本,并可能为应用程序提供解决冲突的方法。尽管MVCC系统可以提供一种灵活的方式来解决写冲突,但在解决方案的开发中会带来极大的复杂性。
尽管Active-Active数据库中的类型和命令看起来与标准Redis类型和命令相同,但RS中的底层类型被增强以维护更多元数据,以创建无冲突的数据类型体验。本节解释了在使用Redis企业软件开发Active-Active数据库时需要了解的内容。
Lua脚本
Active-Active 数据库支持 Lua 脚本,但与标准 Redis 不同,Lua 脚本始终在效果复制模式下执行。目前无法在脚本复制模式下执行它们。
驱逐
Active-Active数据库的默认策略是noeviction模式。Redis Enterprise版本6.0.20及更高版本支持Active-Active数据库的所有驱逐策略,除非启用了Auto Tiering(以前称为Redis on Flash)。 有关详细信息,请参阅Active-Active数据库的驱逐。
过期
支持具有特殊多主语义的过期功能。
如果在Active-Active数据库的不同成员上同时更改一个键的过期时间,则通过TTL设置的较长的扩展时间将被保留。例如:
如果此命令在集群#1的key1上执行
127.0.0.1:6379> EXPIRE key1 10
如果此命令在集群#2的key1上执行
127.0.0.1:6379> EXPIRE key1 50
EXPIRE命令将键设置为50将会生效。
如果此命令在集群#3的key1上执行:
127.0.0.1:6379> PERSIST key1
它将在托管Active-Active数据库的三个集群中胜出,因为它将key1的TTL设置为无限时间。
负责“获胜”过期值的副本也负责在此时过期键并传播DEL效果。从这一点开始,“失败”的副本不再负责过期键,除非另一个EXPIRE命令重置TTL。此外,不是过期值“所有者”的副本:
-
如果用户尝试以读取模式访问该键,则静默忽略该键,例如将其视为已过期但不传播删除操作。
-
如果用户尝试以WRITE模式访问它,则在修改之前使其过期(发送DEL)。
注意:过期值的范围对于Active-Active数据库是[0, 2^49],对于非Active-Active数据库是[0, 2^64]。
内存不足 (OOM)
如果Active-Active数据库的成员处于内存不足的情况,该成员会被RS标记为“不一致”,该成员停止响应用户流量,并且同步器会启动与Active-Active数据库中其他对等节点的完全协调。
主动-主动数据库键计数
对于Active-Active数据库,键的计数方式不同:
- DBSIZE(在
shard-cli dbsize
中)报告在复制冲突解决之前表示键的多个潜在值的键头实例。 - expired_keys(在
bdb-cli info
中)可能超过DBSIZE中的键计数(在shard-cli dbsize
中),因为当一个键变成墓碑时,过期并不总是被移除。 墓碑是一个逻辑上被删除但仍然占用内存的键,直到被垃圾收集器回收。 - Expires 平均 TTL(在
bdb-cli info
中)仅针对本地过期进行计算。
信息
INFO 命令有一个额外的 crdt 部分,提供了高级的故障排除信息(适用于支持等):
部分 | 字段 | 描述 |
---|---|---|
CRDT 上下文 | crdt_config_version | 当前活动的 Active-Active 数据库配置版本。 |
crdt_slots | 此分片分配和报告的哈希槽。 | |
crdt_replid | 唯一的副本/分片ID。 | |
crdt_clock | 本地向量时钟的时钟值。 | |
crdt_ovc | 本地观察到的Active-Active数据库向量时钟。 | |
Peers | 当前连接的Peer Replication对等节点列表。这与Redis报告的从节点列表类似。 | |
待办事项 | 当前维护的Peer Replication待办事项列表。通常在完全网状拓扑中,所有对等方只使用一个待办事项,因为请求的ID是相同的。 | |
CRDT 统计 | crdt_sync_full | 执行的入站完全同步进程的数量。 |
crdt_sync_partial_ok | 执行的基于积压的部分重新同步过程的数量。 | |
crdt_sync_partial-err | 由于积压耗尽而导致的部分重新同步过程失败的数量。 | |
crdt_merge_reqs | 处理的入站合并请求数量。 | |
crdt_effect_reqs | 处理的入站效果请求数量。 | |
crdt_ovc_filtered_effect_reqs | 由于旧向量时钟而被过滤的入站效果请求的数量。 | |
crdt_gc_pending | 等待垃圾回收的元素数量。 | |
crdt_gc_attempted | 尝试垃圾收集墓碑的次数。 | |
crdt_gc_collected | 成功收集的墓碑数量。 | |
crdt_gc_gvc_min | 最小全局观察到的向量时钟,根据所有接收到的观察时钟在本地计算得出。 | |
crdt_stale_released_with_merge | 表示最后一次陈旧标志的转换是由于完整的全同步结果。 | |
CRDT 副本 | 一个 crdt_replica |
|
config_version | 报告的最后配置版本。 | |
shards | 分片数量。 | |
slots | 哈希槽的总数。 | |
slot_coverage | 一个标志,表示远程分片提供完整覆盖(即所有分片都处于活动状态)。 | |
max_ops_lag | 尚未被最少更新的远程分片观察到的本地操作数量 | |
min_ops_lag | 尚未被最新远程分片观察到的本地操作数量 |