java架构之路-(Redis专题)聊聊大厂那些redis
上几次说了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的更多相关文章
- java架构之路(多线程)大厂方式手写单例模式
上期回顾: 上次博客我们说了我们的volatile关键字,我们知道volatile可以保证我们变量被修改马上刷回主存,并且可以有效的防止指令重排序,思想就是加了我们的内存屏障,再后面的多线程博客里还有 ...
- [转帖]java架构之路-(面试篇)JVM虚拟机面试大全
java架构之路-(面试篇)JVM虚拟机面试大全 https://www.cnblogs.com/cxiaocai/p/11634918.html 下文连接比较多啊,都是我过整理的博客,很多答案都 ...
- java架构之路-(Redis专题)简单聊聊redis分布式锁
这次我们来简单说说分布式锁,我记得过去我也过一篇JMM的内存一致性算法,就是说拿到锁的可以继续操作,没拿到的自旋等待. 思路与场景 我们在Zookeeper中提到过分布式锁,这里我们先用redis实现 ...
- java架构之路-(Redis专题)Redis的主从、哨兵和集群
我们使用的redis,单机的绝对做不到高可用的,万一单机的redis宕机了,就没有备用的了,我们可以采用集群的方式来保证我们的高可用操作. 主从架构 大致就是这样的,一个主节点,两个从节点(一般两个就 ...
- java架构之路-(Redis专题)SpringBoot连接Redis超简单
上次我们搭建了Redis的主从架构,哨兵架构以及我们的集群架构,但是我们一直还未投入到实战中去,这次我们用jedis和springboot两种方式来操作一下我们的redis 主从架构 如何配置我上次已 ...
- java架构之路-(Redis专题)Redis的高性能和持久化
上次我们简单的说了一下我们的redis的安装和使用,这次我们来说说redis为什么那么快和持久化数据 在我们现有的redis中(5.0.*之前的版本),Redis都是单线程的,那么单线程的Redis为 ...
- java架构之路-(Redis专题)redis面试助力满分+
1.Redis支持的数据类型? 答:五种,在第一节redis相关的博客我就说过,String,Hash,List,Set,zSet,也就是我们的字符串,哈希,列表,集合,有序集合五种.结构图如下. 2 ...
- java架构之路-(微服务专题)初步认识微服务与nacos初步搭建
历史演变: 以前我们都是一个war包,包含了很多很多的代码,反正我开始工作的时候做的就是这样的项目,一个金融系统,代码具体多少行记不清楚了,内部功能超多,但是实际能用到的不多,代码冗余超大,每次部署大 ...
- java架构之路-(MQ专题)RabbitMQ安装和基本使用
RabbitMQ安装 我这里安装是使用阿里云的CentOS7.5来安装的,使用CentOS版本低于7的可能会报错. 1.安装rabbitmq所需要的依赖包 输入$ yum install build- ...
随机推荐
- Spring入门教程
Spring新手入门教程,配套下面这两个大神的课程就可以了. 一个是Spring视频教程. 一个是Spring博客教程. https://www.imooc.com/learn/196 http:// ...
- 【linux】【Go】Centos7安装go1.13环境
前言 Go(又称Golang)是Google开发的一种静态强类型.编译型.并发型,并具有垃圾回收功能的编程语言. 罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pi ...
- 浅谈 Vector
目录 浅谈Vector 1.容器基本操作 2.vector 初始化 3.vector的赋值与swap 4.vector的增删改除 1.增加元素 2.访问元素 3.删除元素 4.元素的大小 浅谈Vect ...
- net core WebApi——定时任务Quartz
目录 前言 Quartz 测试 问题及解决方法 小结 前言 本来打算昨天都开始写这篇,就因为要把小团队的博客整理汇总,一看二哈的博客那么多,一个个复制粘贴肯定麻烦(其实是我自己觉得复制麻烦),所以穿插 ...
- NABCD分析 [团队任务]
N(Need,需求) 学校有许多闲置的自己用不着或者想出手的二手物品,加群发消息寻找物品太过繁琐,同样兼职信息在QQ群混杂在一起尤为不便.因此我们打算做一个专门发布信息的App.包括发布闲置物品,兼职 ...
- 【django】form
form 组件组要功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 1.创建Form类时,主要涉及到 [字段] 和 [插件],字段用于 ...
- springboot新版本(2.0.0+)自定义ErrorController中使用ErrorAttributes
2.0.0之前使用: @Autowired private ErrorAttributes errorAttributes; private Map<String, Object> get ...
- CSS div仿table样式
要想让div元素显示的样式与table相同,那么需要使用display属性,这个属性将告诉浏览器这些数据是制表的,将以哪种样式来渲染数据: // table --使该元素按table样式渲染 // t ...
- Golang:线程 和 协程 的区别
作者:林冠宏 / 指尖下的幽灵 博客:http://www.cnblogs.com/linguanh/ GitHub : https://github.com/af913337456/ 掘金:http ...
- web前端之移动端:知识汇
移动前端自适应适配方法总结 移动端前端适配方案(总结) -- 面试重点 不要再问我移动适配的问题了 一.响应式布局: // Extra small devices (portrait phones, ...