你必须知道的4种Redis集群方案及优缺点对比为了让服务高可用,分布式服务出现,同一个服务部署在多台机器上,即使有几台服务器宕机,只要一台服务器是可用,服务可用。redis也是一样。为了解决单机故障,引入了主从模式,但是主从模式存在一个问题:主节点出现故障后,需要手动将服务从从节点切换到主节点上服务恢复前的节点。为了解决这个问题,redis引入了哨兵模式。哨兵模式可以在主节点故障后自动将从节点提升为主节点,无需人工干预即可恢复服务。但是无论是主从模式还是哨兵模式都没有实现真正的数据分片存储。每个redis实例存储全量数据,于是rediscluster诞生,实现真正的数据分片存储。但是由于redis集群发布时间比较晚(2015年才发布正式版),各大厂商都等不及了,纷纷研发了自己的redis数据分片集群模型,如:Twemproxy、Codis等。1.虽然redis单节点主从模式可以通过RDB和AOF持久化机制将数据持久化到硬盘,数据存储在一台服务器上。如果服务器出现硬盘故障等问题,会导致数据不可用,读写不能分离,读写都在同一台服务器上,请求量大时会出现I/O瓶颈。为了避免单点故障和读写不分离,Redis提供了复制功能,实现主库数据更新后,更新后的数据会自动同步到其他从库。上面redis主从结构的特点:一个master可以有多个slave节点;一个从节点可以有从节点,从节点是级联结构。主从模式的优缺点优点:主从结构的优点是读写分离,提高效率,数据备份,提供多副本。缺点:最大的缺点就是主从模式没有自动容错和恢复功能。如果master节点出现故障,集群就无法工作,可用性比较低。从节点升级为主节点需要人工干预。普通主从模式,当主库崩溃时,需要手动切换从库成为主库:在从库中使用SLAVENOONE命令,将从库提升为主数据继续服务。启动之前崩溃的主库,然后使用SLAVEOF命令将其设置为新主库的从库来同步数据。2.哨兵模式第一种主从同步/复制模式,当主服务器宕机时,需要手动将一个从服务器切换为主服务器,需要人工干预,费力费力,而且会导致服务失败一段时间使用,这个时候需要哨兵模式出现。sentinel模式是从Redis2.6版本开始提供的,但是当时这个版本的模式不稳定,直到Redis2.8版本才稳定下来。哨兵模式的核心仍然是主从复制,但是相对于主从模式,当主节点宕机无法写入时,多了一个选举机制:从所有从节点中选出一个新的主节点.选举机制的实现依赖于系统中启动一个哨兵进程。如上图所示,Sentry本身存在单点故障问题,所以在一主多从的Redis系统中,可以使用多个Sentinels进行监控。哨兵不仅会监控主库和从库,还会相互监控。每个哨兵都是一个独立的进程,作为一个进程,它会独立运行。(1)哨兵模式的作用:监控所有服务器是否正常运行:通过发送命令返回监控服务器的运行状态,对主从服务器进行处理和监控,哨兵之间也相互监控。Failover:当sentinel检测到master宕机时,会自动将slave切换为master,然后通过发布-订阅的方式通知其他slave服务器,修改配置文件,让它们切换为master。同时,有问题的老主人也会成为新主人的奴隶,也就是说即使旧主人恢复了,也不会恢复原来的主人身份,而是成为新主人的奴隶.(2)Sentinel实现原理Sentinel在启动进程时会读取配置文件的内容,通过如下配置找出要监控的主数据库:sentinelmonitormaster-nameipportquorum#master-nameisthenamemaindatabase#ip和port是当前主数据库地址和端口号#quorum表示在执行故障转移操作之前有多少哨兵节点同意。这里之所以只需要连接主节点,是因为主节点的info命令是用来获取从节点信息的,从而与从节点建立连接,同时知道新加入的节点的信息通过主节点的info信息添加从节点。一个哨兵节点可以监控多个主节点,但不推荐这样做,因为当哨兵节点崩溃时,多个集群切换会同时失败。Sentinel启动后,会与主数据库建立两个连接。订阅主数据库的_sentinel_:hello频道,获取同时监控数据库的sentinel节点信息。定时向主库发送info命令,获取主库本身的信息。与master数据库建立连接后,会定时执行以下三个操作:(1)每10s向master和slave发送info命令。作用是获取当前数据库信息。例如,当发现一个新的从节点时,会建立一个连接并添加到监控列表中。当主从数据库的角色发生变化时,信息也会更新。(2)每隔2s向主数据和从数据库的_sentinel_:hello通道发送自己的信息。作用是将自己的监控数据共享给哨兵。每个sentinel都会订阅数据库的_sentinel:hello频道。当其他哨兵收到消息后,会判断该哨兵是否为新哨兵。如果是,则将其添加到哨兵列表并建立连接。(3)每隔1s向所有主从节点和所有sentinel节点发送ping命令,监测节点是否存活。(3)主观下线和客观下线Sentinel节点发送ping命令,如果节点在一定时间后(down-after-millisecond)没有回复,则sentinel认为主观下线。主观下线是指当前Sentinel认为该节点已经宕机。如果该节点是master数据库,Sentinel会进一步判断是否需要对其进行故障转移。这时候它会发送命令(SENTINELis-master-down-by-addr)询问其他sentinel节点是否主观认为master节点下线。当达到指定的数量(quorum)时,sentinel会客观地认为它下线了。当主节点客观下线时,需要进行主从切换。主从切换的步骤是:选择leadingsentinel。leadersentinel的所有slave选择优先级最高的slave数据库。优先级可以通过slave-priority选项设置。如果优先级相同,与复制命令的偏移量越大(即复制和同步的数据越多,数据越新),优先级越高。如果以上条件相同,则选择runID较小的从库。选择一个从库后,哨兵发送slavenoone命令升级到主库,并发送slaveof命令将其他从节点的主库设置为新的主库。(四)sentinel模式的优缺点1.优点sentinel模式是在主从模式的基础上,解决了主从模式下master故障不能自动切换的问题。2、不足——集中式集群实现的问题:始终只有一台Redis主机接收和处理写请求,写操作受单机瓶颈影响。集群中所有节点存储全量数据,浪费内存空间,没有真正实现分布式存储。当数据量过大时,主从同步严重影响master的性能。Redis主机宕机后,sentinel模式不是在投票的情况下,因为在投票结束之前没人知道主机和从机是谁。这时候Redis也会开启保护机制禁止写操作,直到选出新的Redis主机。主从模式或哨兵模式下每个节点存储的数据都是全量数据。当数据量过大时,需要将存储的数据分段存储在多个redis实例中。这时候就会用到RedisSharding技术。3、各大厂商的Redis集群方案Redis在3.0版本之前只支持单实例模式。尽管Redis的开发者Antirez早在他的博客中就提出要在Redis3.0版本中加入集群功能,但直到2015年正式版才发布3.0版本。各大企业都等不及了。在3.0版本发布之前,为了解决Redis的存储瓶颈,他们纷纷推出了自己的Redis集群解决方案。这些方案的核心思想是在多个Redis实例中存储数据分片(sharding),每个shard就是一个Redis实例。(1)客户端分片客户端分片是将分片的逻辑放在Redis客户端上实现(例如:jedis已经支持RedisSharding功能,即ShardedJedis),通过Redis客户端预先定义的路由规则(使用Consistenthashing),将对Key的访问转发给不同的Redis实例,并在查询数据时收集返回的结果。此场景的架构如图所示。客户端分片的优缺点:优点:客户端分片技术采用哈希一致性算法分片的优点是所有逻辑可控,不依赖第三方分布式中间件。服务端的Redis实例相互独立,互不相关。每个Redis实例都像单个服务器一样运行。非常容易线性扩展,系统非常灵活。开发者知道如何实现sharding和routing的规则,就不用担心踩坑了。1.ConsistentHashAlgorithm:是分布式系统中常用的一种算法。例如,分布式存储系统需要将数据存储在特定的节点上。如果使用普通的hash方法将数据映射到特定的节点,比如mod(key,d),key就是数据的key,d就是机器节点的个数。如果一台机器加入或退出集群,所有的数据映射都会失效。一致性哈希算法解决了普通余数哈希算法扩展性差的问题,在服务器在线或离线的情况下都能保证尽可能多的请求命中原路由的服务器。2、实现方式:一致性哈希算法,如MURMUR_HASH哈希算法,ketamahash算法,如Jedis的RedisSharding实现,采用一致性哈希算法(consistenthashing),同时对key和节点名进行哈希,然后进行映射匹配,使用的算法是MURMUR_HASH。使用一致性哈希而不是简单的类似哈希的模映射的主要原因是当添加或删除节点时,不会因为重新匹配而重新哈希。一致性哈希只影响相邻节点的key分布,影响较小。缺点:这是一种静态分片方案,需要增减Redis实例数,手动调整分片方案。运维成本相对较高。集群数据出现任何问题都需要运维人员和开发人员的配合,拖慢了解决问题的速度,增加了跨部门沟通的成本。在不同的客户端程序中,维护相同的路由分片逻辑的代价是巨大的。比如java项目和PHP项目共用一套Redis集群,路由分片逻辑需要写两套相同的逻辑,以后还会有两套维护。客户端分片最大的问题之一是,当服务端Redis实例组的拓扑结构发生变化时,每个客户端都需要更新和调整。如果能把clientsharding模块单独拿出来,形成一个单独的模块(中间件),这个问题就可以作为client和server之间的桥梁来解决,这时候proxysharding就出现了。(2)代理分片目前使用最广泛的redis代理分片是Twemproxy,Twitter开源的Redis代理。基本原理是:Redis客户端通过中间件的形式将请求发送给Twemproxy,Twemproxy再发送给正确的Redis实例,最后Twemproxy收集结果返回给客户端。Twemproxy引入代理层统一管理多个Redis实例,使得Redis客户端只需要对Twemproxy进行操作,不需要关心后面有多少个Redis实例,从而实现了Redis集群。Twemproxy的优点:客户端像Redis实例一样连接到Twemproxy,不需要改变任何代码逻辑。支持自动删除无效的Redis实例。Twemproxy维护与Redis实例的连接,减少客户端与Redis实例之间的连接数。Twemproxy的不足:由于Redis客户端的每次请求都是通过Twemproxy代理到达Redis服务器,这个过程中会产生性能损耗。没有友好的监控管理后台界面,不利于运维监控。Twemproxy最大的痛点是不能平滑扩容/缩容。对于运维人员来说,由于业务需要,在添加Redis实例时,工作量是非常大的。Twemproxy作为目前使用最广泛、成熟稳定的Redis代理,在业界应用广泛。(3)CodisTwemproxy无法平滑增加Redis实例的问题带来了极大的不便,于是Peapod自主研发了Codis,一款支持平滑增加Redis实例的Redis代理软件,基于Go和C语言开发,于2008年上线2014年11月在GitHub上开源。在Codis的架构图中,Codis引入了RedisServerGroup,通过指定一个masterCodisRedis和一个或多个slaveCodisRedis来实现Redis集群的高可用。当一个主CodisRedis挂掉时,Codis不会自动提升一个从CodisRedis到主CodisRedis,这涉及到数据一致性问题(Redis自身的数据同步采用主从异步复制,当数据成功写入主CodisRedis时,没有保证slaveCodisRedis是否读取过该数据),管理员需要在管理界面手动将slaveCodisRedis提升为主CodisRedis。如果手动处理麻烦,豌豆荚还提供了一个工具Codis-ha,当检测到masterCodisRedis宕机时,会下线,将一个slaveCodisRedis提升为masterCodisRedis。Codis采用预分片的形式。启动时,会创建1024个槽。一个插槽相当于一个盒子。每个盒子都有一个固定的编号,范围从1到1024。插槽盒子用于存储Key。至于Key存放在哪个盒子里,可以通过算法“crc32(key)%1024”得到一个数字。这个数字的范围必须在1到1024之间,Key将放在相应的数字槽中。比如一个Key通过“crc32(key)%1024”算法得到的个数是5,就把它放到编码为5的slot(box)中。一个slot只能放一个RedisServerGroup,一个slot不能放在多个Redis服务器组中。一个RedisServerGroup最少可以存储1个slot,最多可以存储1024个slot。因此,在Codis中最多可以指定1024个RedisServerGroup。Codis最大的优势是支持RedisServerGroup(Redis实例)的平滑增(减),可以安全透明的迁移数据。这也是Codis与Twe??mproxy等静态分布式Redis解决方案的区别。Codis加入RedisServerGroup后,涉及到slot的迁移。比如系统有两个RedisServerGroup,RedisServerGroups和slot的对应关系如下。添加Redis服务器组时,将重新分配插槽。Codis分配slot有两种方式:第一种:通过Codis管理工具Codisconfig手动重新分配,指定每个RedisServerGroup对应的slot范围,例如:可以指定新的RedisServerGroup和RedisServerGroup对应的slot插槽如下。第二种方式:通过Codis管理工具Codisconfig的rebalance功能,根据各个RedisServerGroup的内存自动迁移槽位,实现数据平衡。4、RedisCluster的Sentinel模式虽然可以实现高可用和读写分离,但是有几个不足:Sentinel模式下,每个Redis服务器存储相同的数据,是内存空间的浪费;数据量过大,master同步slave时严重影响master的性能。Sentinel模式是一种集中式集群实现方案,每台slave机器与宿主机高度耦合,从master宕机到slave选举master恢复期间服务不可用。Sentinel模式始终只有一台Redis主机来接收和处理写请求。写操作仍然受到单机瓶颈的影响,没有实现真正的分布式架构。Redis在3.0上加入了Cluster集群模式,实现了Redis的分布式存储,也就是说在每个Redis节点上存储了不同的数据。为了解决单机Redis容量有限的问题,集群模式将数据按照一定的规则分布到多台机器上。内存/QPS不局限于单机,可以受益于分布式集群的高扩展性。RedisCluster是一种服务器分片技术(分片和路由在服务器端实现),采用多主多从。每个分区由一个Redis主机和多个从机组成。在平行下。RedisCluster集群采用P2P模型,完全去中心化。如上图,官方建议集群部署至少需要3个master节点,最好使用6个节点的三主三从模式。RedisCluster集群具有以下特点:集群完全去中心化,采用多主多从;所有redis节点相互连接(PING-PONG机制),内部采用二进制协议优化传输速度和带宽。客户端直接连接到Redis节点,没有中间代理层。客户端不需要连接到集群中的所有节点,而是可以连接到集群中任何可用的节点。每个分区由一台Redis主机和多个从机组成,分片与分片相互并行。每个主节点负责维护一部分slots和映射到slots的key-value数据;集群中的每个节点都有全量的slot信息,通过slots,每个节点都知道具体的数据存储在哪个节点上。Redis集群主要针对海量数据+高并发+高可用场景,海量数据,如果你的数据量很大,那么推荐使用redis集群,数据量不大的时候,使用sentinel就够了。redis集群的性能和高可用优于sentinel模式。RedisCluster采用虚拟hash槽分区代替一致性hash算法,预先分配一些卡槽。根据散列函数将所有键映射到这些槽。每个分区中的主节点负责维护一部分槽和映射的槽。键值数据。
