redis的主从复制,哨兵和cluster集群

一、redis性能管理

(1) redis-cli
 127.0.0.1:6379> info memory
 ​
 
 (2) redis-cli info memory

  • used_memory_rss:是Redis向操作系统申请的内存。
  • used_memory:是Redis中的数据占用的内存。
  • used_memory_peak:redis内存使用的峰值。

redis的主从复制,哨兵和cluster集群_第1张图片

1.2内存碎片

内存碎片率=Redis向操作系统申请的内存 / Redis中的数据占用的内存

mem_fragmentation_ratio = used_memory_rss / used_memory

mem_fragmentation_ratio:内存碎片率。

redis-cli info memory |grep ratio
 

redis的主从复制,哨兵和cluster集群_第2张图片

内存碎片产生的原因

  • Redis内部有自已的内存管理器,为了提高内存使用的效率,来对内存的申请和释放进行管理。
  • Redis中的值删除的时候,并没有把内存直接释放、交还给操作系统,而是交给了Redis内部有内存管理器。
  • Redis中申请内存的时候,也是先看自己的内存管理器中是否有足够的内存可用。
  • Redis的这种机制,提高了内存的使用率,但是会使Redis中有部分自己没在用,却不释放的内存,导致了内存碎片的发生。

内存碎片率对redis的影响 

  • 内存碎片率在1到1.5之间是正常的,这个值表示内存碎片率比较低,也说明Redis 没有发生内存交换。
  • 内存碎片率超过1.5,说明Redis消耗了实际需要的物理内存的150%,其中50%是内存碎片率。
  • 内存碎片率低于1的,说明Redis内存分配超出了物理内存,操作系统正在进行内存交换(使用虚拟内存,会降低性能)。需要增加可用物理内存或减少Redis内存占用。

解决碎片率过大的方法 

  • 如果你的Redis版本是4.0以下的,需要在redis-cli 工具上输入shutdown save命令,让Redis数据库执行保存操作并关闭Redis服务,再重启服务器。Redis服务器重启后,Redis 会将没用的内存归还给操作系统,碎片率会降下来。
  • Redis4.0版本开始,可以在不重启的情况下,线上整理内存碎片,将未使用的内存归还给操作系统。

 config set activedefrag yes    #自动碎片清理
 memory purge                   #手动碎片清理

redis的主从复制,哨兵和cluster集群_第3张图片

 1.3内存使用率 

redis实例的内存使用率超过可用最大内存,操作系统将开始进行内存与swap空间交换。

避免内存交换发生的方法:

  • 针对缓存数据大小选择安装Redis 实例
  • 尽可能的使用Hash数据结构存储
  • 设置key的过期时间

1.4内回收key 

存清理策略,保证合理分配redis有限的内存资源。

当内存使用达到设置的最大阈值时,需选择一种key的回收策略,默认情况下回收策略是禁止删除(noenviction)。配置文件中修改 maxmemory-policy 属性值:

im /etc/redis/6379.conf
 ---598行----
 maxmemory-policy noenviction   #修改max-memory-policy属性值
 ​
 ##回收策略有以下几种:##
 ●volatile-lru
 #使用LRU算法从已设置过期时间的数据集合中淘汰数据
 (移除最近最少使用的key,针对设置了TTL的key)
 ​
 ●volatile-ttl
 #从已设置过期时间的数据集合中挑选即将过期的数据淘汰
 (移除最近过期的key)
 ​
 ●volatile-random
 #从已设置过期时间的数据集合中随机挑选数据淘汰
 (在设置了TTL的key里随机移除)
 ​
 ●allkeys-lru
 #使用LRU算法 从所有数据集合中淘汰数据
 (移除最少使用的key,针对所有的key)
 ​
 ●allkeys-random
 #从数据集合中任意选择数据淘汰(随机移除key)
 ​
 ●noenviction
 #禁止淘汰数据(不删除直到写满时报错)

二、redis的优化策略

2.1设置Redis客户端连接的超时时间 

vim /etc/redis/6379.conf
 -----114行------
 114 timeout 0     
 #单位为秒(s),取值范围为0~100000。默认值为0,表示无限制,即Redis不会主动断开连接,即使这个客户端已经空闲了很长时间。
 #例如可设置为600,则客户端空闲10分钟后,Redis会主动断开连接。
 ​
 #注意:在实际运行中,为了提高性能,Redis不一定会精确地按照timeout的值规定的时间来断开符合条件的空闲连接,例如设置timeout为10s,但空闲连接可能在12s后,服务器中新增很多连接时才会被断开。

2.2 设置redis自动碎片清理

 config set activedefrag yes    #自动碎片清理
 memory purge                   #手动碎片清理

2.3设置redis最大内存阈值 

内存阈值如果不设置,则没有限制,直到把服务器的内存干满、之后会使用交换分区。

设置内存阈值后,不会使用swap交换分区。且如果设置了key回收策略,当内存使用达到设置的最大阈值时,系统会进行key回收。

vim /etc/redis/6379.conf
 -----567行------
 567 # maxmemory
 568 maxmemory 1gb           #例如设置最大内存阈值为1gb

三、redis雪崩、穿透、击穿的原因和解决方案

(1) redis雪崩

定义:缓存雪崩是指大量的应用请求无法在 Redis 缓存中进行处理,紧接着,应用将大量请求发送到数据库层,导致数据库层的压力激增。 

一个简单的雪崩过程:

  1. Redis 集群产生了大面积故障;

  2. 缓存失败,此时仍有大量请求去访问 Redis 缓存服务器;

  3. 在大量 Redis 请求失败后,这些请求将会去访问数据库;

  4. 由于应用的设计依赖于数据库和 Redis 服务,很快就会造成服务器集群的雪崩,最终导致整个系统的瘫痪。

 产生的原因:

1)缓存中有大量数据同时过期,导致大量请求无法得到处理。

2)Redis 缓存实例发生故障宕机了

解决方案: 

  • 【事前】高可用缓存:高可用缓存是防止出现整个缓存故障。即使个别节点,机器甚至机房都关闭,系统仍然可以提供服务,Redis 哨兵(Sentinel) 和 Redis 集群(Cluster) 都可以做到高可用;

  • 【事中】缓存降级(临时支持):当访问次数急剧增加导致服务出现问题时,我们如何确保服务仍然可用。在国内使用比较多的是 Hystrix,它通过熔断、降级、限流三个手段来降低雪崩发生后的损失。只要确保数据库不死,系统总可以响应请求,每年的春节 12306 我们不都是这么过来的吗?只要还可以响应起码还有抢到票的机会;

  • 【事后】Redis备份和快速预热:Redis数据备份和恢复、快速缓存预热。

(2)redis 击穿

 缓存击穿是指当前热点数据存储到期时,多个线程同时并发访问热点数据。因为缓存刚过期,所有并发请求都会到数据库中查询数据。

解决方法:

  • 将热点数据设置为永不过期;

  • 加互斥锁:互斥锁可以控制查询数据库的线程访问,但这种方案会导致系统的吞吐量下降,需要根据实际情况使用。

 (3)缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起id为-1的数据或者特别大的不存在的数据。有可能是黑客利用漏洞攻击从而去压垮应用的数据库。 

  • 验证拦截:接口层进行校验,如鉴定用户权限,对ID之类的字段做基础的校验,如id<=0的字段直接拦截;
  • 缓存空数据:当数据库查询到的数据为空时,也将这条数据进行缓存,但缓存的有效性设置得要较短,以免影响正常数据的缓存;
  •  使用布隆过滤器:布隆过滤器是一种比较独特数据结构,有一定的误差。当它指定一个数据存在时,它不一定存在,但是当它指定一个数据不存在时,那么它一定是不存在的。

二、redis的高可用的概念

在web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。

高可用的计算公式是1-(宕机时间)/(宕机时间+运行时间)有点类似与网络传输的参数误码率,我们用9的个数表示可用性:

2个9:99%,一年内宕机时长:1%×365天=3.6524天=87.6h

4个9:99.99%,一年内宕机时长:0.01%×365天=52.56min

5个9:99.999%,一年内宕机时长:0.001%*365天=5.265min

11个9:几乎一年宕机时间只有几秒钟

但是在Redis语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢失等

2.1Redis的高可用技术

在Redis中,实现高可用的技术主要包括持久化、主从复制、哨兵和cluster集群,下面分别说明它们的作用,以及解决了什么样的问题。

  • 持久化: 持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。

  • 主从复制: 主从复制是高可用Redis的基础,哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份(和同步),以及对于读操作的负载均衡和简单的故障恢复。

    • 缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
  • 哨兵: 在主从复制的基础上,哨兵实现了自动化的故障恢复。(主挂了,找一个从成为新的主,哨兵节点进行监控)

    • 缺陷:写操作无法负载均衡;存储能力受到单机的限制。
  • Cluster集群: 通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。(6台起步,成双成对,3主3从)

三、redis主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。

默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

3.1主从复制的作用

  • 据冗余: 主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  • 故障恢复: 当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  • 负载均衡: 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 高可用基石: 除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

3.2主从复制流程

1)若启动一个slave机器进程,则它会向Master机器发送一个sync command命令,请求同步连接。

(2)无论是第一次连接还是重新、【】连接,Master机器都会启动一个后台进程,将数据快照保存到数据文件中(执行rdb操作),同时Master还会记录修改数据的所有命令并缓存在数据文件中.

(3)后台进程完成缓存操作之后,Master机器就会向slave机器发送数据文件,slave端机器将数据文件保存到硬盘上,然后将其加载到内存中,接着Master机器就会将修改数据的所有操作一并发送给slave端机器。若slave出现故障导致宕机,则恢复正常后会自动重新连接。

(4)Master机器收到slave端机器的连接后,将其完整的数据文件发送给slave端机器,如果Mater同时收到多个slave发来的同步请求,则Master会在后台启动一个进程以保存数据文件,然后将其发送给所有的slave端机器,确保所有的slave端机器都正常。

四、redis一主二从的部署

实验组件

主从 redis的版本号 IP地址
master redis-5.0.7 192.168.195.100
slave1 redis-5.0.7 192.168.195.200
slave2 redis-5.0.7 192.168.195.101

实验具体步骤 

 实验前准备好三台源码编译安装好的redis虚拟机

步骤一:修改master节点的配置文件 

vim /etc/redis/6379.conf 
 bind 0.0.0.0                      #70行,修改监听地址为0.0.0.0(生产环境中,尤其是多网卡最好填写物理网卡的IP)
 daemonize yes                     #137行,开启守护进程,后台启动 
 logfile /var/log/redis_6379.log   #172行,指定日志文件存放目录
 dir /var/lib/redis/6379           #264行,指定工作目录
 appendonly yes                    #700行,开启AOF持久化功能

/etc/init.d/redis_6379 restart     #重启redis服务

redis的主从复制,哨兵和cluster集群_第4张图片 

步骤二:修改slave节点的配置文件

#修改slave1的配置文件
vim /etc/redis/6379.conf 
 bind 0.0.0.0                        #70行,修改监听地址为0.0.0.0(生产环境中需要填写物理网卡的IP)
 daemonize yes                       #137行,开启守护进程,后台启动
 logfile /var/log/redis_6379.log     #172行,指定日志文件目录
 dir /var/lib/redis/6379             #264行,指定工作目录
 replicaof 192.168.73.105 6379       #288行,指定要同步的Master节点的IP和端口
 appendonly yes                      #700行,修改为yes,开启AOF持久化功能

#将配置文件传给slave2
scp /etc/redis/6379.conf 192.168.73.107:/etc/redis/

/etc/init.d/redis_6379 restart  #重启redis
netstat -natp | grep redis      #查看主从服务器是否已建立连接

redis的主从复制,哨兵和cluster集群_第5张图片

实验测试 

master写入数据 

127.0.0.1:6379> keys *
 
127.0.0.1:6379> set name zhangsan
 
127.0.0.1:6379> get name

redis的主从复制,哨兵和cluster集群_第6张图片

 从节点查询 没有问题

redis的主从复制,哨兵和cluster集群_第7张图片

 五、Redis哨兵模式

主从切换技术的方法是:当服务器宕机后,需要手动一台从机切换为主机,这需要人工干预,不仅费时费力而且还会造成一段时间内服务不可用。为了解决主从复制的缺点,就有了哨兵机制。

 哨兵的核心功能:在主从复制的基础上,哨兵引入了主节点的自动故障转移。

 哨兵模式的组成:

哨兵节点: 哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的redis节点,不存储数据。

数据节点: 主节点和从节点都是数据节点。

redis的主从复制,哨兵和cluster集群_第8张图片

5.1  哨兵模式的作用 

  • 监控: 哨兵会不断地检查主节点和从节点是否运作正常。
  • 自动故障转移: 当主节点不能正常工作时,哨兵会开始自动故障转移操,它会将失效主节点的其中一个从节点升级为新的主节点,并让其它从节点改为复制新的主节点。
  • 通知(提醒): 哨兵可以将故障转移的结果发送给客户端。

此外:哨兵节点也可以是单独独立在其他的主机上,并不需要一定安装redis主从复制的节点服务器上 、

5.2故障转移机制

1、由哨兵节点定期监控发现主节点是否出现了故障

每个哨兵节点每隔1秒会问主节点、从节点及其它哨兵节点发送一次ping命令做一次心检测。如果主节点在一定时间范围内不回复或者是回复一个错误消息,那么这个哨兵就会认为这个主节点主观下线了(单方面的)。当超过半数哨兵节点认为该主节点主观下线了,这样就客观下线了。

2、当主节点出现故障,此时哨兵节点会通过Raft算法(选举算法)实现选举机制共同选举出一个哨兵节点为leader,来负责处理主节点的故障转移和通知。所以整个运行哨兵的集群的数量不得少于3个节点。

3、由leader哨兵节点执行故障转移,过程如下:

  • 将某一个从节点升级为新的主节点,让其它从节点指向新的主节点;
  • 若原主节点恢复也变成从节点,并指向新的主节点;
  • 通知客户端主节点已经更换。

需要特别注意的是,客观下线是主节点才有的概念;如果从节点和哨兵节点发生故障,被哨兵主观下线后,不会再有后续的客观下线和故障转移操作

 5.3哨兵模式中主节点的选拔 

1.过滤掉不健康的(己下线的),没有回复哨兵ping响应的从节点。

2.选择配置文件中从节点优先级配置最高的。(replica-priority,默认值为100)

3.选择复制偏移量最大,也就是复制最完整的从节点。

哨兵的启动依赖于主从模式,所以须把主从模式安装好的情况下再去做哨兵模式。

六、redis哨兵模式的部署(哨兵节点可以单独配置或者放在数据节点一起配置)

主从 redis的版本号 IP地址 哨兵点
master redis-5.0.7 192.168.195.100 Sentinel 1
slave1 redis-5.0.7 192.168.195.200 Sentinel 2
slave2 redis-5.0.7 192.168.195.101 Sentinel 3

实验具体操作步骤 

 在redis主从复制的基础上进行哨兵模式的部署

步骤一:修改哨兵节点的配置文件

哨兵的的配置文件是redis软件中自带的配置 

vim /opt/redis-5.0.7/sentinel.conf
......
protected-mode no                #17行,取消注释,关闭保护模式
port 26379                       #21行,Redis哨兵默认的监听端口
daemonize yes                    #26行,指定sentinel为后台启动
logfile "/var/log/sentinel.log"  #36行,指定日志文件存放路径
dir "/var/lib/redis/6379"        #65行,指定数据库存放路径
sentinel monitor mymaster 192.168.195.100 6379 2  #84行,修改
#指定该哨兵节点监控192.168.195.100:6379这个主节点,该主节点的名称是mymaster。
#最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移

sentinel down-after-milliseconds mymaster 3000  #113行,判定服务器down掉的时间周期,默认30000毫秒(30秒)
sentinel failover-timeout mymaster 180000  #146行,同一个sentinel对同一个master两次failover之间的间隔时间(180秒)

#传给两外2个哨兵节点
scp /opt/redis-5.0.7/sentinel.conf  192.168.195.200:/opt/redis-5.0.7/
scp /opt/redis-5.0.7/sentinel.conf  192.168.195.100:/opt/redis-5.0.7/

步骤二:启动 哨兵模式,查看其监控状态

#启动三台哨兵
cd /opt/redis-5.0.7/
redis-sentinel sentinel.conf &
 
#在哨兵节点查看监控状态
[root@localhost ~]# redis-cli -p 26379 info Sentinel

redis的主从复制,哨兵和cluster集群_第9张图片

实验测试 

 故障模拟

#在Master 上查看redis-server进程号:
[root@localhost ~]# ps -ef | grep redis

#杀死 Master 节点上redis-server的进程号
[root@localhost ~]# kill -9 pid号      #Master节点上redis-server的进程号
[root@localhost ~]# netstat -natp | grep redis

redis的主从复制,哨兵和cluster集群_第10张图片

实验结果

 [root@localhost redis-5.0.7]# tail -f /var/log/sentinel.log
 
 
#新master进行键值对的创建
[root@localhost redis-5.0.7]# redis-cli 
127.0.0.1:6379> set newname lisi
OK
127.0.0.1:6379> get newname
"lisi"
127.0.0.1:6379> 

redis的主从复制,哨兵和cluster集群_第11张图片

redis的主从复制,哨兵和cluster集群_第12张图片

redis的主从复制,哨兵和cluster集群_第13张图片

 七 Redis集群模式

集群,即Redis Cluster,是Redis3.0开始引入的分布式存储方案。

集群由多个节点(Node)组成,Redis的数据分布在这些节点中。集群中的节点分为主节点和从节点:只有主节点负责读写请求和集群信息的维护;从节点只进行主节点数据和状态信息的复制。

7.1集群的作用

(1)数据分区: 数据分区(或称数据分片)是集群最核心的功能。

  • 集群将数据分散到多个节点,一方面突破了Redis单机内存大小的限制,存储容量大大增加;另一方面每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力。
  • Redis单机内存大小受限问题,在介绍持久化和主从复制时都有提及;例如,如果单机内存太大,bgsave和bgrewriteaof的fork操作可能导致主进程阻塞,主从环境下主机切换时可能导致从节点长时间无法提供服务,全量复制阶段主节点的复制缓冲区可能溢出。

(2)高可用: 集群支持主从复制和主节点的自动故障转移(与哨兵类似);当任一节点发生故障时,集群仍然可以对外提供服务。

 通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。

7.2Redis集群的数据分片

Redis集群引入了哈希槽的概念。

Redis集群有16384个哈希槽(编号0-16383)。

集群的每个节点负责一部分哈希槽。

每个Key通过CRC16校验后对16384取余来决定放置哪个哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

以3个节点组成的集群为例:

  • 节点A包含0到5460号哈希槽
  • 节点B包含5461到10922号哈希槽
  • 节点c包含10923到16383号哈希   槽

redis集群的部署

真实生产环境中,redis的cluster集群至少需要六台服务器才能实现 ,如果因为电脑性能问题

可以尝试redis多实例部署

实验组件的部署 

安装包:redis-5.0.7.tar.gz

192.168.195.100   我们就用一台机器来搭建

cd /etc/redis/
mkdir -p redis-cluster/redis600{1..6}   #创建redis集群的工作目录、和每个节点的工作目录
ls -R redis-cluster/

到软件包目录,把redis配置文件分别复制到我们第二步所创建的每个节点目录中。也要把/opt/redis-5.0.7/src下的客户端工具redis-cli和服务端命令redis-server复制到每个节点的目录中

cd /opt/redis-5.0.7/src/
ls
cd /opt/redis-5.0.7/
for i in {1..6}; do cp /opt/redis-5.0.7/redis.conf /etc/redis/redis-cluster/redis600$i; cp /opt/redis-5.0.7/src/redis-server /opt/redis-5.0.7/src/redis-cli /etc/redis/redis-cluster/redis600$i; done      
#使用for循环复制可以节省很多时间
cd /etc/redis/redis-cluster/
ls -R
 

修改每个节点目录中的redis配置文件

cd redis6001
vim redis.conf
 
#bind 127.0.0.1     ##第69行;注释掉监听地址,表示监听任意地址(也可设置为0.0.0.0)
protected-mode no    #修改第88行,关闭保护模式,设置为no
port 6001           #修改第92行,修改监听端口为6001
daemonize yes       #修改第136行,开启后台运行
appendonly yes      #修改第699行,开启AOF持久化
 
 
#集群模式配置
cluster-enabled yes    #第832行取消注释,开启集群模式
cluster-config-file nodes-6001.conf   #第840行,每个集群配置文件,取消注释、修改对应的端口(每个节点都要设置对应的)
cluster-node-timeout 15000   #第846行,集群节点之间通信的超时时间;取消注释

启动redis服务,一定要先进入每个节点目录中去使用redis-server命令启动


cd ../redis6001
ls
for i in {1..6}; do cd /etc/redis/redis-cluster/redis600$i; ./redis-server redis.conf; done
ps -elf |grep redis
redis-cli --cluster create 127.0.0.1:6001 127.0.0.1:6002 127.0.0.1:6003 127.0.0.1:6004 127.0.0.1:6005 127.0.0.1:6006 --cluster-replicas 1                                 #把节点加入到集群当中
--cluster-replicas    指定每个主节点有多少个从节点做主从复制
 
 
redis-cli -p 6001    #登录redis数据库
cluster slots        #查看主从对应关系

redis的主从复制,哨兵和cluster集群_第14张图片

 redis的主从复制,哨兵和cluster集群_第15张图片

redis的主从复制,哨兵和cluster集群_第16张图片

验证,键的插入

redis-cli -p 6001 -c    
-c  实现节点间的跳转,如果不加会报错
set name hj

redis的主从复制,哨兵和cluster集群_第17张图片

  登录到6004节点

redis-cli -p 6004 -c
keys *
get name

redis的主从复制,哨兵和cluster集群_第18张图片

redis的主从复制,哨兵和cluster集群_第19张图片

Redis集群拓展:

数据量过多如何处理?

当数据量过多的情况下,一种简单的方式是升级Redis实例的资源配置,包括增加内存容量、磁盘容量、更好配置的CPU等,但这种情况下Redis使用RDB进行持久化的时候响应会变慢,Redis通过fork子进程来完成数据持久化,但fork在执行时会阻塞主线程,数据量越大,fork的阻塞时间就越长,从而导致Redis响应变慢。

Redis的切片集群可以解决这个问题,也就是启动多个Redis实例来组成一个集群,再按照一定的规则把数据划分为多份,每一份用一个实例来保存,这样客户端只需要访问对应的实例就可以获取数据。在这种情况下fork子进程一般不会给主线程带来较长时间的阻塞,如下图:

redis的主从复制,哨兵和cluster集群_第20张图片

切片集群架构图
将20GB的数据分为4分,每份包含5GB数据,客户端只需要找到对应的实例就可以获取数据,从而减少主线程阻塞的时间。

当数据量过多的时候,可以通过升级Redis实例的资源配置或者通过切片集群的方式。前者实现起来简单粗暴,但这数据量增加的时候,需要的内存也在不断增加,主线程fork子进程就有可能会阻塞,而且该方案受到硬件和成本的限制。相比之下第二种方案是一种扩展性更好的方案,如果想保存更多的数据,仅需要增加Redis实例的个数,不用担心单个实例的硬件和成本限制。在面向百万、千万级别的用户规模时,横向扩展的 Redis 切片集群会是一个非常好的选择。

选择切片集群也是需要解决一些问题的:

  • 数据切片后,在多个实例之间怎么分布?
  • 客户端怎么确定想要访问的实例是哪一个?

Redis采用了Redis Cluster的方案来实现切片集群,具体的Redis Cluster采用了哈希槽(Hash Slot)来处理数据和实例之间的映射关系。在Redis Cluster中,一个切片集群共有16384个哈希槽(为什么Hash Slot的个数是16384),这些哈希槽类似于数据的分区,每个键值对都会根据自己的key被影射到一个哈希槽中,映射步骤如下:

  • 根据键值对key,按照CRC16算法计算一个16bit的值。
  • 用计算的值对16384取模,得到0~16383范围内的模数,每个模数对应一个哈希槽。

这时候可以得到一个key对应的哈希槽了,哈希槽又是如何找到对应的实例的呢?

在部署Redis Cluster的时候,可以通过cluster create命令创建集群,此时Redis会自动把这些槽分布在集群实例上,例如一共有N个实例,那么每个实例包含的槽个数就为16384/N。当然可能存在Redis实例中内存大小配置不一的问题,内存大的实例具有更大的容量。这种情况下可以通过cluster addslots命令手动分配哈希槽。

redis-cli -h 33.33.33.3 –p 6379 cluster addslots 0,1redis-cli -h 33.33.33.4 –p 6379 cluster addslots 2,3redis-cli -h 33.33.33.5 –p 6379 cluster addslots 4
复制代码
复制代码

要注意的是,如果采用cluster addslots的方式手动分配哈希槽,需要将16384个槽全部分配完,否则Redis集群无法正常工作。现在通过哈希槽,切片集群就实现了数据到哈希槽、哈希槽到实例的对应关系,那么客户端如何确定需要访问的实例是哪一个呢?

(二)客户端定位集群中的数据

客户端请求的key可以通过CRC16算法计算得到,但客户端还需要知道哈希槽分布在哪个实例上。在最开始客户端和集群实例建立连接后,实例就会把哈希槽的分配信息发给客户端,实例之间会把自己的哈希槽信息发给和它相连的实例,完成哈希槽的扩散。这样客户端访问任何一个实例的时候,都能获取所有的哈希槽信息。当客户端收到哈希槽的信息后会把哈希槽对应的信息缓存在本地,当客户端发送请求的时候,会先找到key对应的哈希槽,然后就可以给对应的实例发送请求了。

但是,哈希槽和实例的对应关系不是一成不变的,可能会存在新增或者删除的情况,这时候就需要重新分配哈希槽;也可能为了负载均衡,Redis需要把所有的实例重新分布。

虽然实例之间可以互相传递消息以获取最新的哈希槽分配信息,但是客户端无法感知这个变化,就会导致客户端访问的实例可能不是自己所需要的了。

Redis Cluster提供了重定向的机制,当客户端给实例发送数据读写操作的时候,如果这个实例上没有找到对应的数据,此时这个实例就会给客户端返回MOVED命令的相应结果,这个结果中包含了新实例的访问地址,此时客户端需要再给新实例发送操作命令以进行读写操作,MOVED命令如下:

GET hello:key(error) MOVED  33.33.33.33:6379
复制代码
复制代码

返回的信息代表客户端请求的key所在的哈希槽为3333,实际是在33.33.33.33这个实例上,此时客户端只需要向33.33.33.33这个实例发送请求就可以了。

此时也存在一个小问题,哈希槽中对应的数据过多,导致还没有迁移到其他实例,此时客户端就发起了请求,在这种情况下,客户端就对实例发起了请求,如果数据还在对应的实例中,会给客户端返回数据;如果请求的数据已经被转移到其他实例上,客户端就会收到实例返回的ASK命令,该命令表示:哈希槽中数据还在前一种、ASK命令把客户端需要访问的新实例返回了。此时客户端需要给新实例发送ASKING命令以进行请求操作。

值得注意的是ASK信息和MOVED信息不一样,ASK信息并不会更新客户端本地的缓存的哈希槽分配信息,也就是说如果客户端再次访问该哈希槽还是会请求之前的实例,直到数据迁移完成.


你可能感兴趣的:(redis,数据库,java)