5 客户端路由
5.1 moved重定向
每个节点通信共享Redis Cluster中槽和集群中对应节点的关系。
- 客户端向Redis Cluster的任一节点发送命令
- 接收命令的节点再计算自己的槽和对应节点
- 如果保存数据的槽被分配给当前节点,则去槽中执行命令,并把命令执行结果返回给客户端
如果保存数据的槽不在当前节点的管理范围内,则向客户端返回moved重定向异常
- 客户端接收到节点返回的结果,如果是moved异常,则从moved异常中获取目标节点的信息
- 客户端向目标节点发送命令,获取命令执行结果
客户端不会自动找到目标节点执行命令,需要二次执行
5.2 ask重定向
由于集群伸缩时,需要数据迁移。
- 当客户端访问某key,节点告诉客户端key在源节点,再去源节点访问时,却发现key已迁移到目标节点,就会返回ask。
- 客户端向目标节点发送命令,目标节点中的槽已经迁移到其它节点
- 目标节点会返回ask转向给客户端
- 客户端向新节点发送Asking命令
- 再向新节点发送命令
- 新节点执行命令,把命令执行结果返回给客户端
为什么不能简单使用MOVED重定向?
虽然MOVED意味着我们认为哈希槽由另一个节点永久提供,并且应该对指定节点尝试下一个查询,所以ASK意味着仅将下一个查询发送到指定节点。
之所以需要这样做,是因为下一个关于哈希槽的查询可能是关于仍在A中的键的,因此我们始终希望客户端尝试A,然后在需要时尝试B。由于只有16384个可用的哈希槽中有一个发生,因此群集上的性能下降是可以接受的。
5.3 moved V.S ask
都是客户端重定向:
- moved:槽已经确定转移
- ask:槽还在迁移中
5.4 智能客户端
目标
追求性能
设计思路
- 从集群中选一个可运行节点,使用
Cluster slots
初始化槽和节点映射 - 将Cluster slots的结果映射在本地,为每个节点创建JedisPool,然后就可以进行数据读写操作
注意事项
- 每个JedisPool中缓存了slot和节点node的关系
- key和slot的关系:对key进行CRC16规则进行hash后与16383取余得到的结果就是槽
- JedisCluster启动时,已经知道key,slot和node之间的关系,可以找到目标节点
- JedisCluster对目标节点发送命令,目标节点直接响应给JedisCluster
- 如果JedisCluster与目标节点连接出错,则JedisCluster会知道连接的节点是一个错误的节点
- 此时JedisCluster会随机节点发送命令,随机节点返回moved异常给JedisCluster
- JedisCluster会重新初始化slot与node节点的缓存关系,然后向新的目标节点发送命令,目标命令执行命令并向JedisCluster响应
- 如果命令发送次数超过5次,则抛出异常"Too many cluster redirection!"
- 基本图示
- 全面图示
6 批量操作
mget、mset须在同一槽。
Redis Cluster 不同于 Redis 单节点,甚至和一个 Sentinel 监控的主从模式也不一样。主要因为集群自动分片,将一个key 映射到16384槽之一,这些槽分布在多节点。因此操作多 key 的命令必须保证所有的key都映射同一槽,避免跨槽执行错误。
一个单独的集群节点,只服务一组专用的keys,请求一个命令到一个Server,只能得到该Server上拥有keys的对应结果。
一个非常简单的例子是执行KEYS命令,当发布该命令到集群中某节点时,只能得到该节点上拥有key,并非集群中所有key。要得到集群中所有key,必须从集群的所有主节点上获取所有key。
对于分散在redis集群中不同节点的数据,如何比较高效地批量获取数据呢?
6.1 串行mget
定义for循环,遍历所有key,分别去所有的Redis节点中获取值并进行汇总,简单,但效率不高,需n次网络时间。
6.2 串行I/O
优化串行的mget,在客户端本地做内聚,对每个key hash,然后取余,知道key对应槽
本地已缓存了槽与节点的对应关系,然后对key按节点进行分组,成立子集,然后使用pipeline把命令发送到对应的node,需要nodes次网络时间,大大减少了网络时间开销。
6.3 并行I/O
优化串行IO,分组key后,根据节点数量启动对应的线程数,根据多线程模式并行向node节点请求数据,只需1次网络时间
6.4 hash_tag
不做任何改变,hash后就比较均匀地散在每个节点上
是否能像单机,一次IO将所有key取出呢?hash-tag提供了这样功能:若将上述key改为如下,即大括号括起来相同的内容,保证所有的key只向一个node请求数据,这样执行类似mget命令只需要去一个节点获取数据即可,效率更高。
6.5 选型对比
第一种方式,使用多线程解决批量问题,减少带宽时延,提高效率,这种做法就如上面所说简单便捷(我们目前批量操作类型比较多),有效。但问题比较明显。批量操作数量不大即可满足。
搜狐的cachecloud采用第二点,先将key获取槽点,然后分node pipeline操作。这种做法相对比第一种做法较优。