redis cluster节点间采起gossip协议进行通讯,也就是说,在每个节点间,不管主节点仍是从节点,他们之间都是存在相互通讯的。例如你的redis端口号是6379,那么你的gossip协议端口号就是16379。java
gossip协议包含多种消息,包括ping,pong,meet,fail等等。node
ping:每一个节点都会频繁给其余节点发送ping,其中包含本身的状态还有本身维护的集群元数据,互相经过ping交换元 数据;面试
pong: 返回ping和meet,包含本身的状态和其余信息,也能够用于信息广播和更新;redis
fail: 某个节点判断另外一个节点fail以后,就发送fail给其余节点,通知其余节点,指定的节点宕机了。算法
meet:某个节点发送meet给新加入的节点,让新节点加入集群中,而后新节点就会开始与其余节点进行通讯,不须要 发送造成网络的所需的全部CLUSTER MEET命令。发送CLUSTER MEET消息以便每一个节点可以达到其余每一个节点只需通 过一条已知的节点链就够了。因为在心跳包中会交换gossip信息,将会建立节点间缺失的连接。数据库
当咱们的master节点和其slave节点中断,或和其它节点中断时,也就是链接超过了咱们设置的cluster‐node‐timeout的值,这时就会认为咱们的当前的master是不可用的,须要选举了,这时将本身记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST信息到全部节点上(包括其余主从的从节点),其它主节点收到FAILOVER_AUTH_REQUEST信息会给与一个FAILOVER_AUTH_ACK反馈,其它从节点不会有任何反应,当咱们的slave收到ACK反馈达到半数以上时,会当选当前选举内的master节点,其它slave节点不在进行选举,做为该新master的slave节点。广播Pong消息通知其余集群节点。缓存
流程大概就是这样的,还有可能每一个正在选举的slave节点收到的ACK反馈是同样的,这时再次触发一次选举,currentEpoch再加1,流程和上面同样。这里要注意的是,并非每一个slave都在同一时刻向外发送FAILOVER_AUTH_REQUEST信息的,通常数据较新的节点会先发,数据的新旧由SLAVE_RANK来判断,SLAVE_RANK越小,表明数据越新。服务器
缓存穿透网络
咱们在通常大型的互联网项目查询到的数据,都是查询的缓存内的数据,也就是咱们的redis内的数据,可是第一次查询或者说根本不存在的数据,会穿过缓存到达咱们的数据库去查询,若是大量这样的请求过来,咱们的数据库是扛不住的。这就是咱们常说的缓存穿透。处理思路很简单,只要是请求过来的,没有结果。存入缓存设置超时时间,再返回。设置时间是为了保证如今没用到,如今没缓存结果,不表明永远没有缓存结果。架构
@GetMapping(value = "/")public String getIndex(String goods_id){ //优先从缓存去拿 String goods =stringRedisTemplate.opsForValue().get(goods_id); if (goods == null){ //若是拿不到去数据库拿 goods = goodsService.getGoodsById(goods_id);//存入缓存,设置超时时间stringRedisTemplate.opsForValue().set(goods_id,goods,300); }returngoods;}
若是是***来了,一直拿不一样的缓存来请求咱们的项目,这样的思路是不可取的,咱们可使用布隆过滤器来实现阻止缓存击穿问题。
缓存预热
双11要来了,每次双11的0点,会有大批的商品进行交易,若是这些商品不是存在缓存内的,超高的并发(都不用双11,平时的秒杀就够受的),大量的线程会涌入数据库,给数据库形成超大的压力,咱们这时应该提早将这些要秒杀的商品,提早存入到redis当中去,防止大批量的请求直接冲进数据库。这就是咱们提到的缓存预热。
缓存失效
刚才咱们的说了预热,可是我仍是须要设置超时时间的时间的,不设置超时时间的话,你的数据库更新了,而咱们的缓存仍是咱们的最开始的数据,形成数据的不一致。假设咱们在预热的时候将大量的商品设置为300秒超时的时间,开始秒杀....过了300秒了。仍是有必定的并发量,这时全部的缓存都失效了,仍是会有大量的请求进入到咱们的数据库的,因此说我在设置缓存预热时,不要设置同一个时间结束。会形成大量的缓存在同一时间失效,给咱们的后台服务形成巨大压力。
缓存雪崩
有不少项目仍是在停留在使用redis单机的状态,若是说redis不在对咱们的项目服务了,大量的请求会涌入咱们的数据访问层,形成咱们的数据库压力超大,甚至数据库宕机,从致使整个服务的不可用状态。或者说咱们的并发量远远超过咱们的redis吞吐量。也会早成redis的拥塞,其它线程请求redis超时,早成redis假死现象,形成咱们的redis雪崩。这时咱们应该尽力采用高可用的缓存层架构,好比哨兵,好比集群架构,对于并发量超大的状况咱们可使用限流的方式来控制。
若是说,咱们的设置了一个缓存,失效时间为300毫秒,但在失效那一刻,仍是高并发的状态,咱们的服务器压力仍是巨大的,这些高并发的请求进入咱们的数据库,后果可想而知,因此咱们要在这个热点key的重建过程当中,避免大量的请求进入咱们的数据库。咱们能够这样来作,尝试加一把简单的锁。
@GetMapping(value = "/")public String getIndex(String goods_id) throws InterruptedException { //优先从缓存去拿 String goods = stringRedisTemplate.opsForValue().get(goods_id);if (goods == null){//若是拿不到去数据库拿//设置只有一个请求能够进入数据库,其他的线程自旋等待Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock" + goods_id, goods_id, Duration.ofMinutes(3));if(aBoolean){ goods = goodsService.getGoodsById(goods_id);//存入缓存,设置超时时间stringRedisTemplate.opsForValue().set(goods_id,goods,300); }else{//自旋等待50毫秒 Thread.sleep(50);//再次调用该方法,尝试获取数据getIndex(goods_id); } }return goods;}
一些Redis的使用建议
1.建议key设置为服务名:表名或者模块名:表名做为key,便于后期的查找和使用。
2.保证能识别语义的前提下,尽力设置key要简洁,不要过长。
3.不要在key中设置特殊字符,好比空格、换行等字符。
4.redis中不要设置过大的值,一个字符串最大限制512M,但建议通常是要超过10kb大小,list,set,hash,zset不建议超过5000个元素,视状况而定。
5.不要使用keys命令,建议使用scan命令进行替换。
6.建议多使用原生命令,管道等操做尽力减小使用,推荐使用mget,mset这样的命令。
这些优化其实都是围绕着咱们Redis的特性,单线程来讲的,若是说咱们存了一个bigKey或者是一次性塞入了超多的命令,极可能阻塞后面的命令,形成咱们的redis假死现象,也会形成咱们的网络拥塞,占有了更多的带宽。
Redis的清除策略
1.被动删除:当读/写一个已通过期的key时,会触发惰性删除策略,直接删除掉这个过时key
2.主动删除:因为惰性删除策略没法保证冷数据被及时删掉,因此Redis会按期主动淘汰一批已过时的key
3.当前已用内存超过maxmemory限定时,触发主动删除策略。
在redis启动前,咱们就配置了,最大的内存使用maxmemory,当前已用内存超过maxmemory限定时,会触发主动清理策略。
默认策略是volatile-lru,即超过最大内存后,在过时键中使用lru算法进行key的剔除,保证不过 期数据不被删除,可是可能会出现OOM问题。
其余策略以下:
allkeys-lru:根据LRU算法删除键,无论数据有没有设置超时属性,直到腾出足够空间 为止。
allkeys-random:随机删除全部键,直到腾出足够空间为止。
volatile-random: 随机删除过时键,直到腾出足够空间为止。
volatile-ttl:根据键值对象的ttl属性,删除最近将要过时数据。若是没有,回退到
noeviction策略。 noeviction:不会剔除任何数据,拒绝全部写入操做并返回客户端错误信息"(error)OOM command not allowed when used memory",此时Redis只响应读操做。
注意:若是没有配置咱们的maxmemory属性,当咱们的内存写满之后,不会触发任何清除策略,会直接将咱们的数据存放在磁盘上,极具下降咱们的redis性能。
redis差很少就说这么多了,咱们大概简单使用,基础的搭建主从,哨兵,集群,java连接redis,redis的优化这几个角度来说解咱们的redis,后面我会弄一篇redis的面试题,也是围绕这些来说解的,仍是那句话,真正懂得了内部的原理,什么样的面试题都不在话下了...