上几次说了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. centos 搭建SVN服务器简单流程

    yum -y install subversion mkdir -p /work/svn && cd /work/svn //创建版本库 svnadmin create test -- ...

  2. Android 让你的 Room 搭上 RxJava 的顺风车 从重复的代码中解脱出来

    # 什么是 Room ? 谷歌为了帮助开发者解决 Android 架构设计问题,在 Google I/O 2017 发布一套帮助开发者解决 Android 架构设计的方案:Android Archit ...

  3. 新手学习Git之在本地使用Git

    每个开发人员应该都会一个版本管理工具,在Git和SVN中,我选择以Git,以下是我的一些心得 什么是 Git Git是目前世界上最先进的分布式版本控制系统(没有之一). 一.Git安装 1).linu ...

  4. java.nio.ByteBuffer中的flip()、rewind()、compact()等方法的使用和区别

    java.nio.ByteBuffer 1. ByteBuffer中的参数position.limit.capacity.mark含义: position:表示当前指针的位置(下一个要操作的数据元素的 ...

  5. vue-router路由元信息及keep-alive组件级缓存

    路由元信息?(黑人问号脸???)是不是这么官方的解释很多人都会一脸懵?那么我们说meta,是不是很多人恍然大悟,因为在项目中用到或者看到过呢? 是的,路由元信息就是我们定义路由时配置的meta字段:那 ...

  6. Python学习笔记整理总结【Memcache & Redis(基础+主从架构)】

    一.Memcached1.简介Memcached 是一个高性能的分布式内存对象缓存系统,一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度.提高可扩展性.用来存储 ...

  7. windows如何利用计划任务自动关机?

    第一步打开控制面板,然后选择计划任务,打开它 选择创建基本任务 输入任务名称,描述,选择下一步 根据需要选择,我这里选择的是每天,然后选择下一步 选择任务开始时间,然后选择下一步 选择启动程序,然后选 ...

  8. 聚类算法之K-means

    想想常见的分类算法有决策树.Logistic回归.SVM.贝叶斯等.分类作为一种监督学习方法,要求必须事先明确知道各个类别的信息,并且断言所有待分类项都有一个类别与之对应.但是很多时候上述条件得不到满 ...

  9. H5刮刮卡效果

    效果图: 核心就是使用ctx.globalCompositeOperation = 'destination-out'; 全部代码: <!DOCTYPE html> <html> ...

  10. 14.Django基础之jQuery操作cookie

    jquery之cookie操作 定义:让网站服务器把少量数据储存到客户端的硬盘或内存,从客户端的硬盘读取数据的一种技术: 下载与引入:jquery.cookie.js基于jquery:先引入jquer ...