Redis-分片&预分片&Redis集群(Redis Cluster)

分片:

分片是将数据划分为多个部分的方法,可以将数据存储到多台服务器里面,这种方法在解决某些问题时可以获得线性提升。

范围分片:

例如将用户id 0-10000 存到redis 1 中,10001-20000 存到 redis2中。但是这样需要维护一张映射范围表,维护操作代价很高。

hash分片:

使用CRC32哈希函数将键转换为一个数字,在对redis实例数量求模就能直到存储的redis实例。

根据执行分片的位置,可以分为三种分片方式:

客户端分片:客户端使用一致性hash等算法 决定键应该分布到那个节点上。

Redis-分片&预分片&Redis集群(Redis Cluster)_第1张图片

缺点:
  • 这时一种静态的分片方案,增加/减少Redis实例的数量,需要人工调整分片。
  • 可运维性差。
  • 在不同的客户端程序中,维护相同分片逻辑成本巨大。如两套系统共用一套redis集群。使用两种不同语言,为了保证分片逻辑的一致性,需要在两个客户端都实现一次分片逻辑。

代理分片:将客户请求发送到代理上,由代理转发请求到正确的节点上。

Redis-分片&预分片&Redis集群(Redis Cluster)_第2张图片

服务器分片: Redis Cluser(redis集群方式)。

客户端发送查询到一个随机实例,这个实例会保证转发你的查询到正确节点,Redis集群在客户端的帮助下,实现了查询路由的一种混合形式(请求不是直接从redis实例转发到另一个,而是客户端收到重定向以后的节点,再次发送请求到正确的redis实例)

分片的缺点:

  • 涉及多个键的操作通常不支持。如,不能对映射在两个redis实例上的键执行交集(有办法办到,但是不能直接这么做)。
  • 涉及多个键的实例不能使用。
  • 分片的粒度是键,所以不能使用一个很大的键来分片数据集。如很大的有序集合。若这样做实际上没有达到分散数据的目的。
  • 当使用了分片,数据处理变得更为复杂。如需要处理多个RDB/AOF文件,备份数据时需要聚合多个实例和主机的持久化实例。
  • 添加和删除也变得复杂。如redis集群具有运行时动态添加和删除节点的能力来支持透明的在均衡数据,但是客户端分片和代理都不支持这种特性。预分片技术可以解决。

预分片技术:

redis作为缓存,增加和删除节点是一件很棘手的事,使用固定的键和实例映射要简单的多。
数据的需求一直在变化,因此我们需要预留足够多的节点供以后扩容使用。
redis只有相当少的内存占用,而且是轻量级(一个空闲实例只用1mb内存) ,一个简单的解决方案就是开启很多实例(32/64),即时只有一台服务器,使用分片来运行多个reids实例在一台服务器上。 当存储出现增长时,需要更更多服务器时,只需要将一半redis实例搬到新的服务器上。
使用redis复制(主动),就可以在很小或者根本不用停机时间内完成移动数据。

  • 在新的服务器上启动一个空实例。
  • 移动数据,配置新实例为源实例的从服务(复制数据)。
  • 停止客户端。
  • 更新被移动实例的服务器IP地址配置。
  • 向新服务器的从节点发送slaveof No one命令 (使得从服务器关闭复制功能,并从 从服务器转变为主服务器,原来同步所得的数据集不会被丢弃)。
  • 以新的更新配置重启客户端。
  • 最后关闭旧服务器不再使用的实例。

Redis集群(Redis Cluster):

redis cluster 提供一种运行redis的方式,数据被自动分片到多个reids节点。
集群不支持处理多个键的命令,因为这需要在redis节点间移动数据,使得redis集群不能提供像单点redis那样的性能,高负载的表现不可知。

Redis集群的TCP端口

每个redis节点需要两个tcp连接打开,正常的tcp端口用来服务客户端,正常端口号+10000的端口用作集群总线端口。
数据端口用于集群总线。集群总线被节点用于错误检测,配置更新,故障转移授权等。客户不能连接集群总线端口。但是要确保在防火墙中打开了这两个端口,否则redis集群节点不能通信。
命令端口和集群总线端口的偏移量总是10000
为了使Redis集群工作正常,对每个节点:

  1. 用于与客户端通信的端口(默认6379) 需要开放给所有需要连接集群的客户端以及其它集群节点(使用客户端端口进行键迁移)
  2. 集群总线端口(客户端口+10000)必须从所有的本集群其它节点可达。

Redis集群的数据分片:

redis集群没有使用一致性hash。而是使用另一种分片方式,每个键概念上被我们称为hash槽(hash slot)的一部分(键属于hash slot)。
我们使用键的CRC16编码对16384取模来计算一个指定键所属的hash槽。

redis集群有16384个哈希槽 ,如有一个三节点的集群:

  • 节点a包含0-5500的hash slot
  • 节点b包含5501-11000的hash slot
  • 节点c包含11001-16384的hash slot
    这使得在集群中添加和删除节点非常容易。如要添加一个节点d,我需要分别从已存在节点中移动部分hash slot到节点d。当我们删除节点a时,只需要移动hash slot到其它已存在节点(每个节点分配一部分)。然后从集群中删除a。
    因为从一个节点向另一个节点移动hash slot并不需要停止工作。所以添加/删除节点,或者改变节点持有hash slot的百分比,都不需要任何停机时间。
    Redis-分片&预分片&Redis集群(Redis Cluster)_第3张图片

redis集群的主从模型:

为了当部分节点失效时,或者无法与大多数节点通信时仍能保持可用。redis集群采用每个节点拥有1-N个副本(一个主服务器,n-1个从服务器) 。

redis集群的一致性保证

redis集群(AOF也仅能做到秒级一致)不保证强一致性。意味着redis可能会丢掉一些写入请求命令。

redis集群丢失写请求第一个原因是因为采用了异步机制:
  • 客户端向主服务器b写入。
  • 主服务器b回复ok给客户端
  • 主服务器b复制写入操作到从服务器b1,b2。
    我们可以看到,b在回复客户端之前没有等待从服务器的确认,因此这是一个过高的延迟代价。 当b确认了这个写操作,但是发送写操作到从服务器之前,b崩溃了,那么我们就永久的丢失这个写操作。
    这和大多数配置为每秒刷新数据到磁盘的数据库发生的事情一样。我们可以在回复客户端之前强制将数据刷新到磁盘。但是降低系统性能。
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)。

节点超时时间过后,主服务器节点被认为失效,可以用其一个副本来取代。同样地,节点超时时间过后,主服务器节点还不能感知其它主服务器节点的大多数,则进入错误状态,并停止接受写请求。

Redis 创建集群命令:

注意: 可以正常运转的集群需要至少有三个主服务器节点。
port 7000
cluster-enabled yes  //开启集群指令
cluster-config-file nodes.conf //每个实例包含保存这个节点配置的文件的路径
cluster-node-timeout 5000  //节点超时时间(用于网络分割后,对主服务器停止)
appendonly yes //开启AOF持久化(redis中其时默认关闭的)

redis集群方案怎么做?都有哪些方案?

codis
目前用的最多的集群方案

支持在节点数量改变的情况下,旧结点数据可以恢复到新的hash节点。
redis cluster 3.0自带的集群,特点在于其分布式算法不是一致性hash,而是hash槽概念,以及支持节点设置从节点。

多启动一些redis实例:

redis实例占用内存不到1mb。在代码层面起几个毫无关联的redis实例,在代码层对key进行hash计算,然后去对应redis实例操作数据。这种方式对hash层代码要求高,需要考虑节点失效后的替代算法,数据震荡后的自动脚本恢复,实例的监控等。

redis集群方案什么情况下会导致集群不可用?

在a,b,c三个节点的集群,在没有复制模型的情况下,如果节点b失败了,那么整个集群就会以为缺少55001-10000这个范围的hash槽而变得不可用。

redis集群如何选择数据库?

redis集群无法做出数据库选择,默认在0数据库。

缓存穿透:

一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就去后端如数据库去查询。一些恶意的请求会故意查询不存在的key,请求量很大就会对后端系统造成压力。

避免方式:
  1. 对查询结果为空的情况也进行缓存,缓存时间可以设置短一点,或者该key对应的数据insert之后清理缓存。
  2. 对一定不存在的key进行过滤。可以把所有可能存在的key当到一个大的bitmap中,查询时通过该map过滤。

缓存雪崩:

当缓存服务器重启或者大量缓存数据在某一时间端失效。这样在失效的时侯就会给后端系统带来很多大压力。造成系统崩溃。

避免方式:
  1. 在缓存失效后通过加锁或则队列来控制读数据库写缓存的线程数量
  2. 做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2。A1缓存失效时间设置为短期,A2设置为长期。
  3. 观察用户行为。不同的key,设置不同的过期时间,让缓存失效时间尽量均匀。
  4. 进行缓存预热,避免系统刚启动不久还未将大量数据进行缓存而引起的缓存雪崩。
参考资料:

reids集群

你可能感兴趣的:(Redis)