CentOS-7下Redis分区的实现方案(一)
1.单机故障问题的解决方案
1.为什么要分区
从上一篇博客CentOS-7下Redis主从复制+sentinel的搭建过程我们知道,哨兵(sentinel)能够在一定程度上解决单机故障和高可用的问题,但是它并没有解决单机容量的问题,所以我们通过集群的方式,对数据进行分区存储,有效增加可使用的容量。此外,如果访问量极大,即使做了主从复制,也可能会导致单机的压力过大,所以也需要进行分而治之。
所以redis分区有两个目的:
1.分区可以让Redis管理更大的内存,Redis将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存;
2.分区可以让Redis管理更大的内存,Redis将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存
2.数据分区的方式(客户端)
1.业务逻辑划分
如果我们的业务逻辑的界限十分明确,并且各个业务模块并不是很庞大,我们可以按照不同的逻辑,将缓存放到不同的redis实例中。比如把用户信息的缓存放到redis_1,把订单相关的缓存放到redis_2,把商品放到相关的缓存redis_3等等。我们事先就明确好各个业务线,然后不同的业务调用不同的redis实例。如下图所示:
2.使用算法划分
业务数据不能划分或者划分比较困难,我们就不能采用上面所说的业务逻辑划分,而应该采用某些算法来进行划分,主要有以下一些做法:
1.hash与取模算法
利用某些算法,不同的结果调取不同的redis实例,最简单的,我们可以利用hash算法,然后取模,根据取模结果,指向不同的redis实例。
但这种方式有弊端:hash取模的话,值是固定的,如果我们要扩展redis,就要调整取模的数值,造成数据和之前的落点不一致,会出现取不到数据的问题,除非我们对所有redis实例的数据进行清理后,重新分配好。即不容易进行增加集群的数量。
2.随机算法
我们还可以按照随机的方法,根据随机算法得出来的数值,随机的分配到相应的redis实例中去。我们可能会想,如果采用完全随机的方式,那就完全不知道应该哪个redis实例中取值了。其实,这种方式有它固定的使用模式,我们可以用它来实现队列。
3.一致性算法
针对上面两种算法的问题,我们可以采用hash一致性算法来计算每个key的落点。这种方式,我们不在对hash值进行取模。那么问题来了,如果不取模,那我们怎么进行落点的寻找呢?想象一下,如果我们模拟一个圆环,圆环上从0度到360度划分出一个一个的点,每个点代表着一个hash值,此时如果redis实例对应着圆环的各个点(利用可以根据ip取hash),然后存储的时候,我们对key也进行hash,看得到的hash值圆环上的位置,如果在圆环上的位置恰好有一个redis实例,那么我们就直接存到该redis实例中,如果没有,我们就找该key的hash值往下最近的一个redis实例的。解释起来可能有些绕,我们用下面的图来说明一下:
这种方式扩展节点,不需要和第一个一样,进行数据的重排,因为一致性算法结果是一样的,没有取模的过程;但是,同时我们也能够看到,这种方式也有很大的弊端:
1.新增节点会造成新增节点与上一个节点的数据会丢失,从而早晨缓存击穿
2.会有数据倾斜的问题,加入我们hash算出来的值不稀疏,不分散,可能会使大量的数据落到同一个redis实例上。解决这个问题,我们可以考虑用虚拟点来代替。
4.弊端总结
上面的这三种方式,可以解决单机存储容量的问题,但是存在一定的弊端,第一种业务逻辑的划分,我们不容易进行扩展集群;第二、第三种方式又会极其容易的引起单个redis实例的连接过多,同时,由于这是在客户端进行key的落点分配,增加了客户端应用开发的难度,曾加了客户端代码编写的复杂度。
那怎么解决这种问题呢?如果我们能够像使用单机一样来使用redis的集群就好了。那么想想我们访问现在的各种互联网产品,我们并不知道它后面有多少服务器,对的,很多网站使用了nginx等这种反向代理,那我们也可以使用代理的方式,代理后面的redis集群。
3.数据分区的方式(代理)
proxy(代理),使用代理,把整个集群做成黑盒,不再需要应用客户端来做复杂的计算,就和使用单机一样来使用集群。
1.单个代理
2.代理的集群
4.数据分区的方式(查询路由)
客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点。
5.数据分区的弊端
1.不支持聚合操作。例如你不能对两个集合求交集,因为他们可能被存储到不同的Redis实例(并不是完全不能实现,只是说我们没办法直接使用交集指令的指令);
2.不支持事务;
3.分区时动态扩容或缩容可能非常复杂;
6.预分片技术
使用redis,我们是使用它作为缓存还是作为持久化的数据库,我们要清楚的知道。虽然两种使用方式从分区的角度来看是没有什么区别的,当把Redis当做一个持久化的存储(服务)时,一个key必须严格地每次被映射到同一个Redis实例。当把Redis当做一个缓存(服务)时,即使Redis的其中一个节点不可用而把请求转给另外一个Redis实例,也不对我们的系统产生什么影响,我们可用任意的规则更改映射,进而提高系统的高可用(即系统的响应能力)。所以我们得出以下结论:
1.如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。
2.如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化.
所以我们在使用redis作为数据库的时候,可以采用一种预分片的方法。
为防止以后的扩容,最好的办法就是一开始就启动较多实例。即便你只有一台服务器,你也可以一开始就让Redis以分布式的方式运行,使用分区,在同一台服务器上启动多个实例。
一开始就多设置几个Redis实例,例如32或者64个实例,对大多数用户来说这操作起来可能比较麻烦,但是从长久来看做这点牺牲是值得的。
这样的话,当你的数据不断增长,需要更多的Redis服务器时,你需要做的就是仅仅将Redis实例从一台服务迁移到另外一台服务器而已(而不用考虑重新分区的问题)。一旦你添加了另一台服务器,你需要将你一半的Redis实例从第一台机器迁移到第二台机器。
1.使用Redis复制技术,你可以做到极短或者不停机地对用户提供服务:
2.在你新服务器启动一个空Redis实例。
3把新Redis实例配置为原实例的slave节点
4.停止你的客户端
5.更新你客户端配置,以便启用新的redis实例(更新IP)。
6.在新Redis实例中执行SLAVEOF NO ONE命令
7.(更新配置后)重启你的客户端
8.停止你原服务器的Redis实例
7.Redis分区实现的几种技术
1.Redis 集群
Redis集群是自动分片和高可用的首选方案。当Redis集群可用,并且有兼容Redis 集群客户端可用于你的编程语言,Redis 集群将成为Redis分区的实际标准。Redis集群是 query routing 和 client side partitioning的一种混合实现。
2.Twemproxy代理技术
Twemproxy是Twitter维护的(缓存)代理系统,代理Memcached的ASCII协议和Redis协议。它是单线程程序,使用c语言编写,运行起来非常快。它是采用Apache 2.0 license的开源软件。
Twemproxy支持自动分区,如果其代理的其中一个Redis节点不可用时,会自动将该节点排除(这将改变原来的keys-instances的映射关系,所以应该仅在把Redis当缓存时使用Twemproxy)。
Twemproxy本身不存在单点问题,因为你可以启动多个Twemproxy实例,然后让你的客户端去连接任意一个Twemproxy实例。
Twemproxy是Redis客户端和服务器端的一个中间层,由它来处理分区功能应该不算复杂,并且应该算比较可靠的。