Redis Cluster Is Not Redis
当一个项目之前使用了Redis,由于种种原因需要改用 Redis Cluster,你可能会想,这很简单,只需要配置一下就可以了。
YOU ARE WRONG!!!
Redis Client#
要正确访问 Redis Cluster,你需要一个支持 Redis Cluster 的客户端,如果客户端不支持,则无法正常访问。
这里主要是因为在访问 cluster 时,如果请求的 key 不在当前节点,redis 会返回信息让客户端连接拥有数据的节点,如果客户端不支持这个特性则无法正确使用 Redis Cluster。
redis-cli 默认情况下是不支持 cluster 的,最新的文档说可以通过 -c 参数开启 cluster support mode。
Python 的 redis 库 redis-py 在 2021年11月25日才开始支持 Redis Cluster。
Application#
更新了支持 Redis Cluster 的客户端之后,就万事大吉了吗?
YOU ARE WRONG!!!
事情才刚刚开始,由于 Redis Cluster 的特性,每个节点都只存储一部分数据,如果一条命令需要操作多条数据,而这些数据不在一个节点上(准确的说不在一个slot中),Redis 会返回 CROSSSLOT Keys Error 错误。
比如用 MGET 获取多条数据,在 LUA 脚本中操作多条数据,用 SUNION 将多个集合合并在一起,等等等等。。。
这一类问题无法通过 redis 的客户端解决,需要应用程序进行相应的处理。
如果确实需要同时操作多个 key 的数据,则可以使用 hashtags 使多个 key 被 hash 到一个 slot 中,下面两篇文章说明了如果操作:
https://hackernoon.com/resolving-the-crossslot-keys-error-with-redis-cluster-mode-enabled
https://repost.aws/knowledge-center/elasticache-crossslot-keys-error-redis
根据第二篇文章里面的说法,有一些 redis 客户端可以帮助做兼容处理,比如调用 MGET 时,redis 库自动计算每个 key 在哪个节点中,然后分别请求相关的节点再进行汇总返回。
这可以帮助简化一些工作,不过目前没有客户端可以做到完全兼容,像 LUA 脚本中操作多条数据的情况,客户端也无能为力。
第三方库#
有些第三方库也会使用 Redis,相比应用程序,第三方库的代码更难以修改。
我在项目中使用的 Celery,其官方目前还不支持 Redis Cluster,只能自己修改代码或找到其它的hack方法。
Proxy#
redis-cluster-proxy 项目可以代理 redis cluster 并对外提供 redis 服务,使不支持 redis cluster 的客户端可以通过它来操作。
其有一个选项,可以开启 cross-slots 查询,对于 MGET 这一类命令它可以帮助分发并汇总结果。
与客户端的兼容处理一样,不要以为开了这个功能,就可以无缝切换,受制于 redis cluster 的结构设计问题,其也只能兼容一部分命令。
该项目2020年发布了 1.0-beta2 版本,之后就再没有发布版本,活跃度不高,如果要在比较严苛的生产环境使用需要更多的测试。
著名的 Envoy 也是支持 redis cluster 代理的,由于对 Envoy 不熟悉,没有对它进行测试。
Redis Cluster Is Not Redis#
在单机版的 Redis 中,所有的 KEY 都是可以访问的,单个的 redis 命令具有原子性,是不会遇到竞态条件的(即并发是安全的)。
但 Redis Cluster 破坏了这些约定,一条命令只能访问同一个 slot 中的 KEY,如果要同时访问多个 slot 中的内容,则只能自己实现事务控制。
无论是客户端还是Proxy,在进行兼容时,都无法保证命令的原子性,下面是 redis-cluster-proxy 关于 cross-slots queries 的说明:
Cross-slots queries use multiple keys belonging to different slots or even different nodes. Since their execution is not guaranteed to be atomic (so, they can actually break the atomic design of many Redis commands), they are disabled by default.
这就是为什么从 Redis 迁移到 Redis Cluster 会遇到极大的挑战。
测试工具#
为了方便对 Redis Cluster 进行测试,我制作了一个 Docker 镜像:https://hub.docker.com/r/zhangjianao/redis-cluster-proxy
镜像是基于 Grokzen 的项目:https://github.com/Grokzen/docker-redis-cluster 具体的文档可以看他的 README。
我在其中增加了 redis-cluster-proxy 服务,默认监听在 7777 端口,以方便对其进行测试。
补充资料#
https://redis.com/blog/redis-clustering-best-practices-with-keys/