项目地址:https://github.com/doyoubi/undermoon
目标:
- 简单
- 快速
迁移过程基于以下 Redis 命令:
- SCAN
- DUMP
- PTTL
- RESTORE
- DEL
SCAN 命令有一个很好的特性,它可以保证在第一个 SCAN 命令之前设置的所有 key 最终都会返回,但有时会返回多次。我们可以执行 3 阶段迁移来模拟复制。
- 等待 Redis 完成所有命令。
- 将所有读写操作重定向到目标 Redis。如果 key 不存在,则目标 Redis 将需要在处理命令之前从源 Redis 转储 key 的数据。
- 开始扫描并将数据转发到 peer Redis。
详细步骤
- migrating proxy(迁移代理) 通过 PreCheck 命令检查 importing proxy(导入代理) 是否也收到迁移任务。
- migrating proxy 阻塞所有新添加的命令到 Queue,并等待现有命令完成。
- migrating proxy 向 importing proxy 发送 TmpSwitch 命令。收到此命令后,importing proxy 开始处理导入slot(槽)范围内的 key。当命令返回时,migrating proxy 释放 Queue 内的所有命令,并将它们重定向到 importing proxy。
- migrating proxy 使用 SCAN、PTTL、DUMP、RESTORE、DEL 将迁移 slot 范围内的所有数据转发到 peer importing Redis。RESTORE 不设置 REPLACE flag。
- importing proxy 在处理命令时,无论是读操作还是写操作,都会先
- 将 EXISTS 和处理后的命令发送到 local importing Redis,如果 EXISTS 返回 true,则将命令转发到 local importing Redis。
- 如果 EXISTS 返回 false,则发送 DUMP 和 PTTL 到迁移的 Redis 获取数据,并 RESTORE 数据并将命令转发到 local Redis。然后最后将命令转发到 local importing Redis。
- 如果该命令不会删除 key,则获取 key lock,
- 如果该命令可能删除 key,则获取 key lock 并将 UMSYNC 发送到 migrating proxy,让 migrating proxy 使用 DUMP、PTTL、RESTORE、DEL 将 key 传输到 importing proxy。然后最后将命令转发到 local importing Redis。
- 当 migrating proxy 完成扫描后,它会向 importing proxy 提出 CommitSwitch。然后importing proxy只需要在 local Redis 中处理命令。
- 通知 coordinator 并等待 UMCTL SETCLUSTER 的最终提交。
为什么会这样设计
整个迁移过程基于以下命令 SCAN、PTTL、DUMP、RESTORE、DELETE。仅向导入服务器代理发送 RESTORE 命令,因此为了获得更好的性能,应在迁移服务器代理中执行此扫描和传输。
由于扫描和传输在服务器代理和 Redis 上都占用了大量的 CPU 资源,因此最好在importing proxy上处理其他工作负载。因此,一开始我们将所有插槽(slots)直接转移到importing proxy。
此时,importing proxy 仍然只有一小部分数据。当它需要处理新添加的 slots 上的命令时,需要在处理请求之前使用 PTTL、DUMP、RESTORE 从迁移的服务器代理中拉取数据。它还需要发送 DELETE 来删除 key。
请注意,对于不会删除 key 的任何命令,由于它是幂等的,因此对同一 key 多次 RESTORE 仍然是正确的。所以仅仅让 importing proxy 来拉数据不会导致任何不一致。
但是对于那些可能删除 DEL、EXPIRE、KPOP 等 key 的命令,只让 importing proxy 拉取数据可能会导致以下情况:
- key 被删除
- 还有另一个 RESTORE 命令可以恢复 key。
因此,在提取数据时,需要将其与
- importing proxy中的其他 RESTORE 命令。
- migrating proxy中的 SCAN 和 RESTORE。
因此,我们需要在 importing proxy 中锁定 key,并且需要 migrating proxy 帮助我们发送数据而不是从importing proxy中拉取数据,以便对该 key 的操作只能按顺序处理。
性能
因此,在迁移过程中,迁移和导入 proxy 的工作量非常平衡。migrating proxy 使用 130% 的 CPU,importing proxy使用 80% 的 CPU。
而且迁移 1G 数据只用了不到一分钟。
在测试中,在迁移的同时进行基准测试,吞吐量从 50k 减少到 28k 并逐渐增加到 40k。这是因为在迁移和importing proxy中,SCAN、DUMP、RESTORE 会在 Redis 中消耗大量吞吐量。但是一旦 key 被迁移到导入服务器代理,它只需要在请求之前发送一个额外的 EXISTS 命令。
提交迁移后,吞吐量将翻倍。