前几年,redis如果要搭建几个节点,每个节点存储一部分数据,就得用到一些中间件,比如codis或者twemproxy。有一些redis中间件,你读写redis中间件,redis中间件负责把你的数据分布存储在多台机器上的redis实例中。这两年redis一直在不断的发展,不断有redis的新版本。目前的redis集群模式可以在多台机器上部署多个redis实例,每个实例存储一部分数据,每个redismaster实例可以链接到redisslave实例,自动保证如果redismaster实例挂了,它会自动切换到redisslave实例。现在新版本的redis,大家都用rediscluster,也就是redis原生支持的rediscluster模式,所以面试官肯定会拍你几轮rediscluster。如果你没用过rediscluster,那是正常的。以前很多人用codis之类的客户端来支持集群,但至少你得研究一下redis集群。如果你的数据量不大,主要是针对高并发、高性能的场景。比如你的缓存一般只有几个G,单机就够了。您可以使用复制。一个主人有多个奴隶。需要多少奴隶?读吞吐量相关,然后搭建sentinel集群,保证redis主从架构的高可用。Redis集群主要针对海量数据+高并发+高可用的场景。Redis集群支持N个redis主节点,每个主节点可以挂载多个从节点。这样整个redis就可以水平扩展了。如果要支持更大数据量的缓存,那么横向扩展更多的master节点,每个master节点可以存储更多的数据。Redis集群引入自动分片数据,将一部分数据放在每个master上,提供内置的高可用支持。当一些师傅不在时,他们仍然可以继续工作。在redis集群架构下,每个redis需要释放两个端口号,比如一个是6379,一个是加了1w的端口号,比如16379。16379端口号用于节点间通信,即,集群总线,集群总线的通信用于故障检测和配置更新,故障转移授权。集群总线使用另一种二进制协议,即八卦协议,用于节点之间高效的数据交换,占用更少的网络带宽和处理时间。节点间内部通信机制基本通信原理维护集群元数据的方式有两种:集中式和Gossip协议。Redis集群节点使用八卦协议进行通信。中心化就是将几种类型的集群元数据(节点信息、故障等)存储在某个节点上。中心化元数据存储的典型代表是大数据领域的Storm。它是一个分布式大数据实时计算引擎和集中式元数据存储结构。底层基于zookeeper(分布式协调中间件)来存储和维护所有的元数据。Redis使用另一种方式来维护集群元数据,gossip协议,所有节点都持有一份元数据副本,如果不同的节点有元数据发生变化,它们会不断地向其他节点发送元数据,让其他节点也进行元数据的变化。中心化的好处是元数据读取和更新的时效性非常好。一旦元数据发生变化,会立即更新到中心化存储,其他节点读取时可以感知;缺点是,所有的元数据更新压力都集中在一个地方,可能会对元数据存储造成压力。gossip的好处是元数据的更新比较分散,不会集中在一个地方,更新请求会陆续发送到所有节点进行更新,减轻了压力;缺点是更新元数据有延迟,这可能会导致一些操作会有一些滞后。10000端口:每个节点都有一个专门用于节点间通信的端口,就是端口号+10000为自己的服务,比如7001,那么17001端口用于节点间通信。每个节点每隔一段时间向其他几个节点发送ping消息,其他几个节点收到ping后返回pong。交换的信息:信息包括故障信息、节点的增删、哈希槽信息等。gossipprotocolgossip协议包含了多种消息,包括ping、pong、meet、fail等。meet:节点向新加入的节点发送meet,允许新节点加入集群,然后新节点将开始与其他节点通信。实际上,redis-trib.rbadd-node内部会向新加入的节点发送gossipmeet消息,通知该节点加入我们的集群。Ping:每个节点都会频繁的向其他节点发送ping,包括自己的状态和自己维护的集群元数据,通过ping交换元数据。pong:返回ping和meeet,包含自身状态等信息,也用于信息广播和更新。Fail:一个节点判断另一个节点发生故障后,向其他节点发送fail,通知其他节点某个节点宕机了。当ping报文深入ping时,需要携带一些元数据。如果非常频繁,可能会增加网络负载。每个节点每秒执行10次ping,每次都会选择其他5个最长时间未通信的节点。当然,如果发现某个节点的通信延迟达到cluster_node_timeout/2,则立即发送ping,避免数据交换延迟过长,滞后的时间过长。比如两个节点之间有10分钟没有数据交换,那么整个集群就处于元数据严重不一致的情况,就会出现问题。所以cluster_node_timeout是可以调整的,调大了会降低ping的频率。每个ping都会带上自己节点的信息和其他节点信息的1/10,发送出去进行交换。至少包含3个其他节点的信息,最多包含节点总数减去2个其他节点的信息。分布式寻址算法哈希算法(海量缓存重构)一致性哈希算法(自动缓存迁移)+虚拟节点(自动负载均衡)redis集群的哈希槽算法哈希算法自带一个key,先计算出哈希值,然后赋值给节点号取模。然后打不同的master节点。一旦一个主节点宕机,所有的请求都会根据最新的剩余主节点数取模并尝试获取数据。这样会导致大部分的请求过来,都得不到有效的缓存,导致大量的流量涌入数据库。一致性哈希算法一致性哈希算法将整个哈希值空间组织成一个虚拟的环,整个空间按顺时针方向组织。下一步是散列每个主节点(使用服务器的ip或主机名)。这决定了每个节点在其哈希环上的位置。当一个key来的时候,先计算hash值,确定数据在环上的位置,然后从这个位置开始沿着环顺时针“走”,遇到的第一个主节点就是key的位置。在一致性哈希算法中,如果一个节点挂掉了,受影响的数据只有这个节点和环空间中的前一个节点(逆时针走时遇到的第一个节点)之间的数据,其他受影响。添加节点也是如此。烧鹅,当一致性哈希算法节点过少时,容易因为节点分布不均造成缓存热点。为了解决这个热点问题,一致性哈希算法引入了虚拟节点机制,即为每个节点计算多个哈希,在每个计算结果位置放置一个虚拟节点。这样就实现了数据的均匀分布和负载均衡。redis集群的hash槽算法固定有16384个hash槽,对每个key计算CRC16值,然后对16384取模得到key对应的hash槽。redis集群中的每个master都持有一些slot。比如有3个master,每个master可能持有5000多个hashslots。哈希槽使添加和删除节点变得容易。添加master时,将其他master的hash槽移到过去,减少master时,将其hash槽移至其他master。移动哈希槽的成本非常低。客户端的API可以让他们对指定的数据去同一个哈希槽,通过哈希标签实现。如果任何一台机器宕机,其他两个节点不会受到影响。因为密钥是在寻找哈希槽,而不是机器。redis集群的高可用以及主备切换的原理redis集群的高可用原理和sentinel差不多。判断节点宕机如果一个节点认为另一个节点宕机,那么就是pfail,主观宕机。如果多个节点认为另一个节点宕机了,那么就是fail,客观宕机,和Sentinel、sdown、odown的原理几乎一样。在cluster-node-timeout内,如果某个节点还没有返回pong,则认为它是pfail。如果一个节点认为某个节点pfail,就会在gossipping报文中ping其他节点,如果超过半数的节点认为pfail,那么就变成fail。从节点过滤掉宕机的主节点,从自己所有的从节点中选出一个切换为主节点。检查每个从节点与主节点的断开连接时间。如果超过cluster-node-timeout*cluster-slave-validity-factor,那么就没有资格切换到master。从节点选举每个从节点,根据自己对master拷贝过来的数据的偏移量设置一个选举时间。偏移量越大(复制的数据越多)的slave节点选举时间越早,优先选举。所有master节点开始slave选举投票,投票选举出slave。如果大多数主节点(N/2+1)投票给某个从节点,则选举通过,该从节点可以切换为主节点。从节点进行主备倒换,从节点切换到主节点。与sentinel相比,整个过程和sentinel非常相似,所以rediscluster功能强大,直接集成了replication和sentinel的功能。
