Medusar's Blog
敬畏知识,谦逊前行
Toggle navigation
Medusar's Blog
主页
Booklist
Resources
About Me
归档
标签
Redis集群学习
redis
nosql
2016-03-12 11:27:53
581
0
0
medusar
redis
nosql
## Redis 3.0之前的集群方案 3.0之前,Redis并没有提供一套集群方案,我们用redis cluster用的最多的应该是twitter发布的[Twemproxy](https://github.com/twitter/twemproxy),还有就是豌豆荚开发的[codis](https://github.com/wandoulabs/codis)。 上面的两种都是以中间件的形式提供服务。 ## Redis 3.0官方集群方案 Redis集群是一个提供在多个Redis间节点间共享数据的程序集。 **Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.** redis cluster在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。 ###那么redis 是如何合理分配这些节点和数据的呢? Redis 集群没有并使用传统的一致性哈希来分配数据,而是采用另外一种叫做哈希槽(hashslot)的方式来分配的。 Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么: 节点 A 包含 0 到 5500号哈希槽. 节点 B 包含5501 到 11000 号哈希槽. 节点 C 包含11001 到 16384号哈希槽. 这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我像移除节点A,需要将A中得槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态. ## 为什么哈希槽的数量固定为16384?[这里](https://github.com/antirez/redis/issues/2576) 由于使用CRC16算法,该算法可以产生2^16-1=65535个值,可是为什么哈希槽的数量设置成了16384? > 1. Normal heartbeat packets carry the full configuration of a node, that can be replaced in an idempotent way with the old in order to update an old config. This means they contain the slots configuration for a node, in raw form, that uses 2k of space with 16k slots, but would use a prohibitive 8k of space using 65k slots. 2. At the same time it is unlikely that Redis Cluster would scale to more than 1000 mater nodes because of other design tradeoffs. > So 16k was in the right range to ensure enough slots per master with a max of 1000 maters, but a small enough number to propagate the slot configuration as a raw bitmap easily. Note that in small clusters the bitmap would be hard to compress because when N is small the bitmap would have slots/N bits set that is a large percentage of bits set. 总结一下 1. redis的一个节点的心跳信息中需要携带该节点的所有配置信息,而16K大小的槽数量所需要耗费的内存为2K,但如果使用65K个槽,这部分空间将达到8K,心跳信息就会很庞大。 2. Redis集群中主节点的数量基本不可能超过1000个。 3. Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率slots/N很高的话,bitmap的压缩率就很低,所以N表示节点数,如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。而16K个槽当主节点为1000的时候,是刚好比较合理的,既保证了每个节点有足够的哈希槽,又可以很好的利用bitmap。 ## Redis集群的数据一致性问题 Redis 集群不保证强一致性。实践中,这意味着在特定的条件下,Redis集群可能会丢掉一些被系统收到的写入请求命令。一般有两个原因会造成redis集群数据不一致。 ### 原因一:异步复制 Redis 集群为什么会丢失写请求的第一个原因,是因为采用了异步复制。这意味着在写期间下面的事情发生了: * 你的客户端向主服务器 B 写入。 * 主服务器 B 回复 OK 给你的客户端。 * 主服务器 B 传播写入操作到其从服务器 B1,B2 和 B3。 你可以看到,B 在回复客户端之前没有等待从B1,B2,B3的确认,因为这是一个过高的延迟代价,所以如果你的客户端写入什么东西,B 确认了这个写操作,但是在发送写操作到其从服务器前崩溃了,其中一个从服务器被提升为主服务器,永久性的丢失了这个写操作。 这非常类似于在大多数被配置为每秒刷新数据到磁盘的数据库发生的事情一样,这是一个可以根据以往不包括分布式系统的传统数据库系统的经验来推理的场景。同样的,你可以通过在回复客户端之前强制数据库刷新数据到磁盘来改进一致性,但这通常会极大的降低性能。 基本上,有一个性能和一致性之间的权衡。未来,Redis 集群在必要时可能或允许用户执行同步写操作。 ### 原因二:网络分割 Redis 集群丢失写操作还有另一个场景,发生在网络分割时,客户端与至少包含一个主服务器的少数实例被孤立起来了。 举个例子,我们的集群由 A,B,C,A1,B1,C1共6个节点组成,3个主服务器,3个从服务器。还有一个客户端,我们称为 Z1。 分割发生以后,有可能分割的一侧是 A,C,A1,B1,C1,分割的另一侧是 B 和Z1。Z1仍然可以写入到可接受写请求的 B。如果分割在很短的时间内恢复,集群会正常的继续。但是,如果分割持续了足够的时间,B1在分割的大多数这一侧被提升为主服务器,Z1 发送给 B 的写请求会丢失。 注意,Z1 发送给 B 的写操作数量有一个最大窗口:如果分割的大多数侧选举一个从服务器为主服务器后过了足够多的时间,少数侧的每一个主服务器节点将停止接受写请求。这个时间量是Redis集群一个非常重要的配置指令,称为节点超时(node timeout)。 节点超时时间过后,主服务器节点被认为失效,可以用其一个副本来取代。同样地,节点超时时间过后,主服务器节点还不能感知其它主服务器节点的大多数,则进入错误状态,并停止接受写请求。 ## 扩展阅读 1. [Redis官方文档](http://www.redis.cn/topics/cluster-tutorial.html) 2. [Redis集群研究和实践(基于redis 3.0.5)](https://www.zybuluo.com/phper/note/195558) 3. [Redis集群](http://wiki.jikexueyuan.com/project/redis-guide/cluster-a.html) 4. [Redis集群](http://wiki.jikexueyuan.com/project/redis/cluster-up.html) 5. [Redis集群规范](http://www.redis.cn/topics/cluster-spec.html) 6. [Why use 16384](https://github.com/antirez/redis/issues/2576)
上一篇:
Docker 使用入门
下一篇:
Commons-logging启动原理
0
赞
581 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
Please enable JavaScript to view the
comments powered by Disqus.
comments powered by
Disqus
文档导航