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- ...
随机推荐
- C#下载http/https的pdf、excel等文件(非在线打开,绕开插件)
非本服务器文件,如PDF,excel等,下载一般是通过href=‘远程文件的http或者https’的方式下载,但是如果浏览器已经有PDF插件了,则用href不是下载,而是在线打开了,影响体验,所以远 ...
- Pytorch的基础数据类型
引言 本篇介绍Pytorch的基础数据类型,判断方式以及常用向量 基础数据类型 torch.Tensor是一种包含单一数据类型元素的多维矩阵. 目前在1.2版本中有9种类型. 同python相比,py ...
- 提交任务到spark(以wordcount为例)
1.首先需要搭建好hadoop+spark环境,并保证服务正常.本文以wordcount为例. 2.创建源文件,即输入源.hello.txt文件,内容如下: tom jerry henry jim s ...
- 二叉树总结(五)伸展树、B-树和B+树
一.伸展树 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作. 因为,它是一颗二叉排序树,所以,它拥有二叉查找树的性质:除此之外,伸展树还具有的一个特点 ...
- 使用git在github远程仓库中操作
在github上创建一个仓库,这一步参考廖雪峰老师的git教程,以及其他的一些准备工作略,我只记录几个重要的命令. 从其他github地址克隆项目 $ git clone git@github.com ...
- Flask学习之旅--用 Python + Flask 制作一个简单的验证码系统
一.写在前面 现在无论大大小小的网站,基本上都会使用验证码,登录的时候要验证,下载的时候要验证,而使用的验证码也从那些简简单单的字符图形验证码“进化”成了需要进行图文识别的验证码.需要拖动滑块的滑动验 ...
- 基于C#的机器学习--c# .NET中直观的深度学习
在本章中,将会学到: l 如何使用Kelp.Net来执行自己的测试 l 如何编写测试 l 如何对函数进行基准测试 Kelp.Net是一个用c#编写的深度学习库.由于能够将函数链到函数堆栈中,它在 ...
- Ubuntu 启动zookeeper报错
在启动zk客户端连接server时报错: 2019-03-30 23:06:24,915 [myid:localhost:2181] - INFO [main-SendThread(localhost ...
- 【SQL server初级】SQL SERVER Transactional Replication中添加新表如何不初始化整个快照
在SQL SERVER的复制(Replication)中,有可能出现由于业务需求变更,需要新增一张表或一些表到已有的复制(发布订阅)当中,这种需求应该是很正常,也很常见的.但是在已有的复制(发布订阅) ...
- 【ADO.NET基础】加密方法公共类
各种加密方法集锦: using System; using System.Security.Cryptography; using System.Text; using System.IO; usin ...