问
1、问题:
1、新加节点,如果没有分配slot,没有执行redis-trib.rb reshard,
java的java.lang.IllegalArgumentException: Node 10.31.145.246:7032 is unknown to cluster
解决:新加节点需要执行reshard
2、10.46.65.34[ERR] Not all 16384 slots are covered by nodes.
解决:redis.conf配置文件cluster-require-full-coverage 设置为no,重启集群。
槽是否全覆盖:cluster-require-full-coverage no。默认是yes,只要有结点宕机导致16384个槽没全被覆盖,整个集群就全部停止服务,所以一定要改为no。 如果将其设置为yes,则默认情况下,如果key的空间的某个百分比未被任何节点覆盖,则集群停止接受写入。 如果该选项设置为no,则即使只处理关于keys子集的请求,群集仍将提供查询。
遇到个问题…. check节点的时候,会遇到下面的问题…..
>>> Check for open slots… >>> Check slots coverage… [ERR] Not all 16384 slots are covered by nodes. oot@ubuntu:~# redis-cli -c -p 7000 127.0.0.1:7000> set a a (error) CLUSTERDOWN The cluster is down
官方是推荐使用redis-trib.rb fix 来修复集群…. …. 通过cluster nodes看到7001这个节点被干掉了… 那么 /redis-3.0.1/src/redis-trib.rb fix 127.0.0.1:7001 ,如果还是启动不了的话,可以把相关的cluster-config-file节点同步信息删掉。
2.应用案
5.1 有道:Redis Cluster使用经验
详情请参见原文,关键内容摘录如下:
5.1.1 两个缺点
“redis cluster的设计在这块有点奇葩,跟集群相关的操作需要一个外部的ruby脚本来协助(当然可能是为了让主程序的代码足够简洁?),然后那个脚本还只支持填实例的ip不支持host,还不告诉你不支持让你用host之后各种莫名其妙。”
“第一个缺点就是严格依赖客户端driver的成熟度。如果把redis cluster设计成类似Cassandra,请求集群中任何一个节点都可以负责转发请求,client会好写一些。”
“第二个缺点完全是设计问题了,就是一个redis进程既负责读写数据又负责集群交互,虽然设计者已经尽可能简化了代码和逻辑,但还是让redis从一个内存NoSQL变成了一个分布式NoSQL。分布式系统很容易有坑,一旦有坑必须升级redis。”
5.1.2 去中心化 vs. Proxy
“关于redis cluster的设计,Gossip/P2P的去中心化架构本身不是问题,但一旦有了中心节点,能做的事情就多了,比如sharding不均匀是很容易自动rebalance的,而无中心的只能靠外界来搞。然后redis cluster又是slot的形式而非C*式的一致性哈希,新节点分slot又不自动,依赖外界(ruby脚本)来分配显得不方便更不优美和谐。而且因为是master-slave的系统而非W R>N的那种,master挂掉之后尽快发现是比较重要的,gossip对于节点挂掉的发现终究没有中心节点/zookeeper方便快速。”
“基于proxy做转发意味着屏蔽了下层存储,完全可以根据前缀/tag/冷热程度,来把部分甚至大多数数据放在磁盘从而节约成本又保证一致性,这都是有中心节点所带来的好处。”
5.2 奇虎360:Redis Cluster浅析和Bada对比
详情请参见原文,关键内容摘录如下:
5.2.1 负载均衡问题
“redis cluster的主备是以节点为单位,而bada则是以partition为单位,这样,同样是3个节点,1024个partition的情况下,redis cluster的主节点负责整个1024个partition的服务,而两个从节点则只负责异步备份,导致集群负载不均,再看bada,将1024个partition的主均分到3个节点中,每个节点各有主备,主对外提供服务,这样均分了访问压力,有效的利用了资源。”
5.2.2 一致性的保证
“redis cluster与bada一样,最终一致性,读写都只请求主节点,当一条写请求在对应的主节点写成功后,会立刻返回给客户端成功,然后主节点通过异步的方式将新的数据同步到对应的从节点,这样的方式减少了客户端多个节点写成功等待的时间,不过在某些情况下会造成写丢失:
1)当主节点接受一条写请求,写入并返回给客户端成功后不幸宕掉,此时刚才的写还未同步给其对应的从节点,而从节点在发现主节点挂掉并重新选主后,新的主节点则永久丢失了之前老的主节点向用户确认的写
2)当网络发生割裂,将集群分裂成少数派与多数派,这样在客户端不知情的情况下,会将写继续写入到少数派中的某些主节点中,而当割裂超过一定时长后,集群感知到异常,此时少数派中的所有主节点会停止响应所有的写请求,多数派的其对应的从节点则会发起选举成为新的主节点,假设过了一会后割裂恢复,老的主节点发现有更新的主存在,自动变成其从节点,而新的主节点中则会永久丢失掉网络割裂至集群感知异常进行切主这个阶段老主节点确认的所有写
相对于redis cluster的永久丢失,bada通过binlog merge有效的解决了这一问题。所有partition的主节点在响应客户端的写请求时,都会在本地记录binlog,binlog实质就是带有时间戳的KV对。当老主以从节点的身份重新加入集群时,会触发binlog merge操作,新主会比较并且合并二者的binlog,这样就可以将之前丢失掉得写再补回来。”
5.2.3 请求重定向问题
“bada服务端节点在收到本不该由自己负责的Partition请求后,不会向客户端返回重定向信息,而是通过代理的方式,直接在集群内部向正确节点转发客户端的请求,并将结果同meta信息再转发回客户端。”
“再看multi key操作,redis cluster为了追求高性能,支持multi key的前提是所有的key必须在同一个节点中, 不过这样的处理需要交给用户,对需要进行multi key操作的所有key,在写入前人为的加上hash tags。当redis cluster进行resharding的时候,也就是将某些slot从一个节点迁移到另一个节点时,此时的multi key操作可能会失败,因为在迁移的slot中的key此时存在于两个节点。
bada怎么做呢?用户如果对multi key操作性能很在乎时,可以采用与redis cluster同样的方式,给这些key加上hash tags来让它们落在同一个节点,如果可以接受性能的稍微损耗而解放用户的处理逻辑,则可以像single key操作一样,请求任一bada节点,它会代理所有的key请求并将结果返回给用户。并且在multi key操作在任何时候都可以,即使在进行partition的迁移,bada也会提前进行切主,保证服务的正常提供。”
5.3 芒果TV:Redis服务解决方案
详情请参见原文,关键内容摘录如下:
芒果TV在Redis Cluster基础上进行开发,主要增加了两个组件:
- 监控管理:以Python为主要开发框架的Web应用程序Redis-ctl
- 请求代理:以C 11为开发语言的轻量数据代理程序cerberus。其作用和优点为:
- 集群代理程序的自动请求分发/重试机制使得应用不必修改自身代码或更新Redis库
- 代理节点为所有Redis节点加上统一管理和状态监测, 可以查阅历史数据, 或在发生任何问题之后快速响应修复
- 代理进程的无状态性使之可在故障后快速恢复, 不影响后端集群数据完整性
这两个组件都已开源到GitHub上,
6.Pros & Cons总结
关于Redis Cluster带来的种种优势就不说了,在这里主要是“鸡蛋里挑骨头”,总结一下现阶段集群功能的欠缺之处和可能的“坑”。
6.1 无中心化架构
6.1.1 Gossip消息
Gossip消息的网络开销和时延是决定Redis Cluster能够线性扩展的因素之一。关于这个问题,在《redis cluster百万QPS的挑战》一文中有所提及。
6.1.2 结点粒度备份
此外,Redis Cluster也许是为了简化设计采用了Master-Slave复制的数据备份方案,并没有采取如Cassandra或IMDG等对等分布式系统中常见的Slot粒度(或叫Partition/Bucket等)的自动冗余和指派。
这种设计虽然避免比较复杂的分布式技术,但也带来了一些问题:
- Slave完全闲置:即便是读请求也不会被重定向到Slave结点上,Slave属于“冷备”
- 写压力无法分摊:Slave闲置导致的另一个问题就是写压力也都在Master上
6.2 客户端的挑战
由于Redis Cluster的设计,客户端要担负起一部分责任:
- Cluster协议支持:不管Dummy还是Smart模式,都要具备解析Cluster协议的能力
- 网络开销:Dummy客户端不断重定向的网络开销
- 连接维护:Smart客户端对连接到集群中每个结点Socket的维护
- 缓存路由表:Smart客户端Slot路由表的缓存和更新
- 内存消耗:Smart客户端上述维护的信息都是有内存消耗的
- MultiOp有限支持:对于MultiOp,由客户端通过KeyTag保证所有Key都在同一Slot。而即便如此,迁移时也会导致MultiOp失败。同理,对Pipeline和Transaction的支持也受限于必须操作同一Slot内的Key。
6.3 Redis实现问题
尽管属于无中心化架构一类的分布式系统,但不同产品的细节实现和代码质量还是有不少差异的,就比如Redis Cluster有些地方的设计看起来就有一些“奇葩”和简陋:
- 不能自动发现:无Auto Discovery功能。集群建立时以及运行中新增结点时,都要通过手动执行MEET命令或redis-trib.rb脚本添加到集群中
- 不能自动Resharding:不仅不自动,连Resharding算法都没有,要自己计算从哪些结点上迁移多少Slot,然后还是得通过redis-trib.rb操作
- 严重依赖外部redis-trib:如上所述,像集群健康状况检查、结点加入、Resharding等等功能全都抽离到一个Ruby脚本中了。还不清楚上面提到的缺失功能未来是要继续加到这个脚本里还是会集成到集群结点中?redis-trib也许要变成Codis中Dashboard的角色
- 无监控管理UI:即便未来加了UI,像迁移进度这种信息在无中心化设计中很难得到
- 只保证最终一致性:写Master成功后立即返回,如需强一致性,自行通过WAIT命令实现。但对于“脑裂”问题,目前Redis没提供网络恢复后的Merge功能,“脑裂”期间的更新可能丢失
6.4 性能损耗
由于之前手头没有空闲的物理机资源,所以只在虚拟机上做了简单的单机测试,在单独的一台压力机使用YCSB测试框架向虚拟机产生读写负载。虚拟机的配置为8核Intel Xeon CPU X5650@2.67GHz,16GB内存,分别搭建了4结点的单机版Redis和集群版Redis,测试一下Redis Cluster的性能损耗。由于不是最近做的测试,所以Jedis用的2.6.2版本。注:当然Redis Cluster可以通过多机部署获得水平扩展带来的性能提升,这里只是由于环境有限所以做的简单单机测试。
由于YCSB本身仅支持Redis单机版,所以需要我们自己增加扩展插件,具体方法请参照《YCSB性能测试工具使用》。通过YCSB产生2000w随机数据,Value大约100Byte左右。然后通过YCSB测试Read-Mostly(90% Read)和Read-Write-Mixed(50% Read)两种情况:
- 数据加载:吞吐量上有约18%的下降。
- Read-Mostly:吞吐量上有约3.5%~7.9%的下降。
- Read-Write-Mixed:吞吐量上有约3.3%~5.5%下降。
- 内存占用:Jedis客户端多占用380MB内存。
6.5 最后的总结
从现阶段看来,相比Sentinel或Codis等方案,Redis Cluster的优势还真是有限,个人觉得最大的优点有两个:
- 官方提供的Slot实现而不用像Codis那样去改源码了;
- 不用额外的Sentinel集群或类似的代码实现了。
同其他分布式系统,如Cassandra,或内存型的IMDG如Hazelcast和GridGain,除了性能方面外,从功能上Redis Cluster简直被爆得体无完肤… 看看我之前总结过的GridGain介绍《开源IMDG之GridGain》:
- 结点自动发现和Rebalance
- 分区粒度的备份
- 故障时分区角色自动调整
- 结果聚合(不会重定向客户端)
- “脑裂”恢复后的Merge(Hazelcast支持多种合并策略)
- 多Primary分区写操作(见Replicated模式)
这些都是Redis Cluster没有或者要手动完成的。当然这也不足为奇,因为这与Redis的设计初衷有关,毕竟作者都已经说了,最核心的设计目标就是性能、水平伸缩和可用性。
Redis集群根据上述说明,可以了解到,框架是采用P2P的模式,完全去中心化,数据存储模块和分布式的逻辑模块耦合在一起。这样带来的好处是部署非常简单,一体式部署,相对Codis而言,没有太多的其他概念、组件和依赖。但缺点也是比较明显,譬如分布式逻辑出现bug,只能回滚重启整个集群。
同时,我们通过上述可以了解到,Redis集群对协议进行了较大的修改,对客户端的交互升级不少,见上述“MOVED重定向”的客户端实现。由于历史原因,历史的应用均使用传统的Redis API,若业务更换Redis Client,存在不少问题,例如升级工作、数据迁移及测试,所以业内暂未被大规模使用。