上几次说了redis的主从,哨兵,集群配置,但是内部的选举一直没说,先来简单说一下选举吧。

集群选举

  redis cluster节点间采取gossip协议进行通信,也就是说,在每一个节点间,无论主节点还是从节点,他们之间都是存在相互通信的。例如你的redis端口号是6379,那么你的gossip协议端口号就是16379。

  gossip协议包含多种消息,包括ping,pong,meet,fail等等。

  ping:每个节点都会频繁给其他节点发送ping,其中包含自己的状态还有自己维护的集群元数据,互相通过ping交换元 数据;

  pong: 返回ping和meet,包含自己的状态和其他信息,也可以用于信息广播和更新;

  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的优化与实际场景

缓存穿透

  我们在一般大型的互联网项目查询到的数据,都是查询的缓存内的数据,也就是我们的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);
}
return goods;
}

  如果是黑客来了,一直拿不同的缓存来请求我们的项目,这样的思路是不可取的,我们可以使用布隆过滤器来实现阻止缓存击穿问题。

缓存预热

  双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差不多就说这么多了,再深入的c语言代码,我也不懂了,我们大概简单使用,基础的搭建主从,哨兵,集群,java链接redis,redis的优化这几个角度来讲解我们的redis,后面我会弄一篇redis的面试题,也是围绕这些来讲解的,还是那句话,真正懂得了内部的原理,什么样的面试题都不在话下了...

最进弄了一个公众号,小菜技术,欢迎大家的加入

java架构之路-(Redis专题)聊聊大厂那些redis的更多相关文章

  1. java架构之路(多线程)大厂方式手写单例模式

    上期回顾: 上次博客我们说了我们的volatile关键字,我们知道volatile可以保证我们变量被修改马上刷回主存,并且可以有效的防止指令重排序,思想就是加了我们的内存屏障,再后面的多线程博客里还有 ...

  2. [转帖]java架构之路-(面试篇)JVM虚拟机面试大全

    java架构之路-(面试篇)JVM虚拟机面试大全 https://www.cnblogs.com/cxiaocai/p/11634918.html   下文连接比较多啊,都是我过整理的博客,很多答案都 ...

  3. java架构之路-(Redis专题)简单聊聊redis分布式锁

    这次我们来简单说说分布式锁,我记得过去我也过一篇JMM的内存一致性算法,就是说拿到锁的可以继续操作,没拿到的自旋等待. 思路与场景 我们在Zookeeper中提到过分布式锁,这里我们先用redis实现 ...

  4. java架构之路-(Redis专题)Redis的主从、哨兵和集群

    我们使用的redis,单机的绝对做不到高可用的,万一单机的redis宕机了,就没有备用的了,我们可以采用集群的方式来保证我们的高可用操作. 主从架构 大致就是这样的,一个主节点,两个从节点(一般两个就 ...

  5. java架构之路-(Redis专题)SpringBoot连接Redis超简单

    上次我们搭建了Redis的主从架构,哨兵架构以及我们的集群架构,但是我们一直还未投入到实战中去,这次我们用jedis和springboot两种方式来操作一下我们的redis 主从架构 如何配置我上次已 ...

  6. java架构之路-(Redis专题)Redis的高性能和持久化

    上次我们简单的说了一下我们的redis的安装和使用,这次我们来说说redis为什么那么快和持久化数据 在我们现有的redis中(5.0.*之前的版本),Redis都是单线程的,那么单线程的Redis为 ...

  7. java架构之路-(Redis专题)redis面试助力满分+

    1.Redis支持的数据类型? 答:五种,在第一节redis相关的博客我就说过,String,Hash,List,Set,zSet,也就是我们的字符串,哈希,列表,集合,有序集合五种.结构图如下. 2 ...

  8. java架构之路-(微服务专题)初步认识微服务与nacos初步搭建

    历史演变: 以前我们都是一个war包,包含了很多很多的代码,反正我开始工作的时候做的就是这样的项目,一个金融系统,代码具体多少行记不清楚了,内部功能超多,但是实际能用到的不多,代码冗余超大,每次部署大 ...

  9. java架构之路-(MQ专题)RabbitMQ安装和基本使用

    RabbitMQ安装 我这里安装是使用阿里云的CentOS7.5来安装的,使用CentOS版本低于7的可能会报错. 1.安装rabbitmq所需要的依赖包 输入$ yum install build- ...

随机推荐

  1. Spring入门教程

    Spring新手入门教程,配套下面这两个大神的课程就可以了. 一个是Spring视频教程. 一个是Spring博客教程. https://www.imooc.com/learn/196 http:// ...

  2. 【linux】【Go】Centos7安装go1.13环境

    前言     Go(又称Golang)是Google开发的一种静态强类型.编译型.并发型,并具有垃圾回收功能的编程语言. 罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pi ...

  3. 浅谈 Vector

    目录 浅谈Vector 1.容器基本操作 2.vector 初始化 3.vector的赋值与swap 4.vector的增删改除 1.增加元素 2.访问元素 3.删除元素 4.元素的大小 浅谈Vect ...

  4. net core WebApi——定时任务Quartz

    目录 前言 Quartz 测试 问题及解决方法 小结 前言 本来打算昨天都开始写这篇,就因为要把小团队的博客整理汇总,一看二哈的博客那么多,一个个复制粘贴肯定麻烦(其实是我自己觉得复制麻烦),所以穿插 ...

  5. NABCD分析 [团队任务]

    N(Need,需求) 学校有许多闲置的自己用不着或者想出手的二手物品,加群发消息寻找物品太过繁琐,同样兼职信息在QQ群混杂在一起尤为不便.因此我们打算做一个专门发布信息的App.包括发布闲置物品,兼职 ...

  6. 【django】form

    form 组件组要功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 1.创建Form类时,主要涉及到 [字段] 和 [插件],字段用于 ...

  7. springboot新版本(2.0.0+)自定义ErrorController中使用ErrorAttributes

    2.0.0之前使用: @Autowired private ErrorAttributes errorAttributes; private Map<String, Object> get ...

  8. CSS div仿table样式

    要想让div元素显示的样式与table相同,那么需要使用display属性,这个属性将告诉浏览器这些数据是制表的,将以哪种样式来渲染数据: // table --使该元素按table样式渲染 // t ...

  9. Golang:线程 和 协程 的区别

    作者:林冠宏 / 指尖下的幽灵 博客:http://www.cnblogs.com/linguanh/ GitHub : https://github.com/af913337456/ 掘金:http ...

  10. web前端之移动端:知识汇

    移动前端自适应适配方法总结 移动端前端适配方案(总结) -- 面试重点 不要再问我移动适配的问题了 一.响应式布局: // Extra small devices (portrait phones, ...