JAVA面试八股文之Redis相关

2024-04-09 1202阅读

Redis相关

  • Redis6.0为什么要用多线程?
  • 在Redis中存一个list集合怎么实现排序?
  • Redis的5大基本类型的底层原理?
  • 缓存穿透?
  • 缓存击穿?
  • 缓存雪崩?
  • redis做为缓存怎么保持和mysql数据进行同步?(双写一致性)
  • redis做为缓存,数据的持久化是怎么做的?
  • Redis的数据过期策略有哪些 ?
  • Redis的数据淘汰策略有哪些 ?
  • 数据库有1000万数据 ,Redis只能缓存20w数据, 如何保证Redis中的数据都是热点数据 ?
  • Redis分布式锁如何实现 ?
  • 如何控制Redis实现分布式锁有效时长呢?
  • redisson实现的分布式锁是可重入的吗?
  • redisson实现的分布式锁能解决主从一致性的问题吗?
  • 如果业务非要保证数据的强一致性,这个该怎么解决呢?
  • Redis集群有哪些方案?
  • 什么是主从同步?
  • 主从同步数据的流程
  • 怎么保证Redis的高并发高可用?
  • redis集群脑裂,该怎么解决呢?
  • redis的分片集群有什么作用?
  • Redis分片集群中数据是怎么存储和读取的?
  • Redis是单线程的,但是为什么还那么快?
  • 能解释一下I/O多路复用模型?

    JAVA面试八股文之Redis相关

    Redis6.0为什么要用多线程?

    提高请求数,redis的瓶颈在于内存,使用分一个子线程去进行IO操作。

    在Redis中存一个list集合怎么实现排序?

    list类型中有一个sort命令可以排序。(sort key)

    Redis的5大基本类型的底层原理?

    Redis底层使用一个redisObject来表示所有的key和value

    JAVA面试八股文之Redis相关

    type:表示属于哪种基本类型;

    encoding:表示底层使用数据结构;

    ptr:指向底层数据结构的指针;

    vm:虚拟内存,默认是关闭的;

    • String:动态字符串SDS
      1. int:字符串类型的整型,底层使用int实现,比如set a 100;
      2. raw:动态字符串,大于39字节,分配两次;
      3. embstr:动态字符串,小于39字节,内存类型,只分配一次;
      • List 类型:链表
        1. zipList:列表的长度小于512,元素的字节数小于64,使用ziplist,它进行了压缩;
        2. linkedList:不满足上面的条件就使用linkedlist,双向链表;
        • set类型:intset或者hashtable
          1. intest:集合长度512且元素都是整数,使用intset;
          2. hashtble:hashtable底层使用字典结构(hash),键为字符串对象,value为null;
          • hash类型:ziplist或者hashtable
            1. 键值对数量小于512,且所有键值对长度小于64字节,使用ziplist压缩;
            2. 键值对数量大于512,且其中有键值对长度大于64字节,使用hashtable;
            • zset类型:ziplist或者skiplist
              1. 集合长度小于128,元素的字节数小于64使用ziplist压缩;
              2. 上面不满足使用skiplist,底层使用跳表实现;

              缓存穿透?

              查询到空数据,缓存中没有,数据库中也没有,当有大量的空数据访问,会造成数据库压力过大,容易宕机。

              解决方法:

              1.缓存空数据:查到数据库中也没有,在缓存中缓存一个空数据,下次访问直接从数据库中获取

              2.布隆过滤器:用于检索一个数据是否存在于一个集合中,(底层主要是先去初始化一个比较大数组,里面存放的二进制0或1。在一开始都是0,当一个key来了之后经过3次hash计算,模于数组长度找到数据的下标然后把数组中原来的0改为1,这样的话,三个数组的位置就能标明一个key的存在。)访问时先判断布隆过滤器中是否有数据,没有则直接返回。不过存在误判问题。

              缓存击穿?

              对于设置了过期时间的数据,缓存在某个时间点过期,而恰好这个时间有大量的数据进行访问。

              解决方法:

              双重锁

              互斥锁:在缓存中没有查询到时,先获取互斥锁,查询数据库重建缓存,然后释放锁。此时其他线程只能等待。

              (强一致性,性能差)

              逻辑过期:在缓存中没有查询到时,一个线程先获取到互斥锁,再开一个线程去查询数据库重建缓存,其他线程则返回过期的时间。(高可用,性能优)

              缓存雪崩?

              大量的缓存在同一时间失效,或者redis宕机,请求直接到数据库。

              解决方法:

              给key的过期时间设置一个随机值,

              搭建redis集群,

              给业务添加多级缓存,

              给业务添加降级限流策略。

              redis做为缓存怎么保持和mysql数据进行同步?(双写一致性)

              存强一致性的

              • redisson实现的读写锁,在读的时候添加共享锁,可以保证读读不互斥,读写互斥。当我们更新数据的时候,添加排他锁,它是读写,读读都互斥,这样就能保证在写数据的同时是不会让其他线程读数据的,避免了脏数据。这里面需要注意的是读方法和写方法上需要使用同一把锁才行。(强一致性)

                允许延时一致的业务

                • 写入数据库后,清空缓存。

                • 设置过期时间来保证最终一致性。

                • 使用canal同步(阿里的canal组件实现数据同步:不需要更改业务代码,部署一个canal服务。canal服务把自己伪装成mysql的一个从节点,当mysql数据更新以后,canal会读取binlog数据,然后在通过canal的客户端获取到数据,更新缓存即可。)

                • 使用MQ中间中间件,更新数据之后,通知缓存删除。

                  先删除缓存或者删除数据库都会出现问题,所有需要延时双删。

                  JAVA面试八股文之Redis相关

                  redis做为缓存,数据的持久化是怎么做的?

                  redis的数据持久化有两种,RDB和AOF

                  RDB:是以快照的形式把redis存储在内存的数据转到磁盘上去,当redis宕机恢复后,再从快照文件中加载数据。(二进制文件,体积小,恢复快)

                  快照分为手动快照和自动快照:

                  手动快照分为前台快照(save)和后台快照(bgsave);

                  AOF:含义是追加文件,当redis操作写命令的时候,都会存储这个文件中,当redis实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据,因为AOF文件中保存的都是命令,所以必然会出现重复的命令,Redis采用重写机制来解决这个问题,重写要解决的问题就是删除AOF文件中重复的命令,已经被删除或者过期的命令。(默认关闭)

                  JAVA面试八股文之Redis相关

                  RDB缺点:容易丢失数据,耗时效率低;

                  RDB优点:适用于数据备份,方便传输,恢复大数据快,最大化redis性能(后台快照)

                  AOF优点:数据更加耐久(一旦出现故障最多丢失一秒数据),只进行追加的日志文件,体积过大会进行重写,容易读懂;

                  AOF缺点:体积大,速度低;

                  实际是两个一起用;

                  Redis的数据过期策略有哪些 ?

                  惰性删除和定期删除

                  惰性删除:当key过期时,只有当需要key时,去查询key判断是否过期,当过期就删除该key。

                  定期删除:每隔一段时间就对一些key检查,过期就删除。

                  Redis的数据淘汰策略有哪些 ?

                  这个在redis中提供了很多种,默认是noeviction,不删除任何数据,内存不足直接报错。

                  是可以在redis的配置文件中进行设置的,里面有两个非常重要的概念,一个是LRU,另外一个是LFU。

                  LRU的意思就是最少最近使用,用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。

                  LFU的意思是最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。

                  数据库有1000万数据 ,Redis只能缓存20w数据, 如何保证Redis中的数据都是热点数据 ?

                  可以使用 allkeys-lru (挑选最近最少使用的数据淘汰)淘汰策略,那留下来的都是经常访问的热点数据。

                  Redis分布式锁如何实现 ?

                  在redis中提供了一个命令setnx(SET if not exists)

                  由于redis的单线程的,用了命令之后,只能有一个客户端对某一个key设置值,只有当redis中没有这个key时才可以添加。(在没有过期或删除key的时候是其他客户端是不能设置这个key的)

                  如何控制Redis实现分布式锁有效时长呢?

                  采用的redis的一个框架redisson实现的。在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成的时候,在redisson中引入了一个看门狗机制,就是说每隔一段时间(每个锁默认时间是30s,如果30s之内业务代码没有执行完,会有定时任务自动续期,(1/3的时间)10s中续期1次,续的的时间还是30s)就检查当前业务是否还持有锁,如果持有就增加加锁的持有时间,当业务执行完成之后需要使用释放锁就可以了。

                  JAVA面试八股文之Redis相关

                  还有一个好处就是,在高并发下,一个业务有可能会执行很快,先客户1持有锁的时候,客户2来了以后并不会马上拒绝,它会自旋不断尝试获取锁,如果客户1释放之后,客户2就可以马上持有锁,性能也得到了提升。

                  /**
                  1、加锁,默认的超时时间是看门狗的时间(30s)
                  2、自动给锁续期(原理是只要占锁成功会启动一个定时任务给锁重新设置超时时间,默认续期的超时时
                  间是看门狗的时间,定时任务会在三分之一的看门狗的时间被触发一次)
                  3、获取不到锁阻塞阻塞
                  4、锁一旦释放了就不在续期
                  **/
                  lock();
                  /**
                  1、加锁,用户指定获取锁等待的时间
                  2、会自动给锁续期
                  3、等待指定的时间后返回布尔值
                  // 一般使用这个方法比较多,锁的超时时间就是业务执行的最大时间。超过这个时间说明业务执行出
                  问题了,应该马上释放锁,因为续期没有意义了。
                  */
                  lock(10, TimeUnit.SECONDS); 
                  

                  redisson实现的分布式锁是可重入的吗?

                  是可以重入的。这样做是为了避免死锁的产生。这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计算上减一。在存储数据的时候采用的hash结构,大key可以按照自己的业务进行定制,其中小key是当前线程的唯一标识,value是当前线程重入的次数。

                  redisson实现的分布式锁能解决主从一致性的问题吗?

                  这个是不能的,比如,当线程1加锁成功后,master节点数据会异步复制到slave节点,此时当前持有Redis锁的master节点宕机,slave节点被提升为新的master节点,假如现在来了一个线程2,再次加锁,会在新的master节点上加锁成功,这个时候就会出现两个节点同时持有一把锁的问题。

                  我们可以利用redisson提供的红锁来解决这个问题,它的主要作用是,不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁,并且要求在大多数redis节点上都成功创建锁,红锁中要求是redis的节点数量要过半。这样就能避免线程1加锁成功后master节点宕机导致线程2成功加锁到新的master节点上的问题了。

                  但是,如果使用了红锁,因为需要同时在多个节点上都添加锁,性能就变的很低了,并且运维维护成本也非常高,所以,我们一般在项目中也不会直接使用红锁,并且官方也暂时废弃了这个红锁。

                  如果业务非要保证数据的强一致性,这个该怎么解决呢?

                  redis本身就是支持高可用的,做到强一致性,就非常影响性能,所以,如果有强一致性要求高的业务,建议使用zookeeper实现的分布式锁,它是可以保证强一致性的。

                  Redis集群有哪些方案?

                  在Redis中提供的集群方案总共有三种:主从复制、哨兵模式、Redis分片(cluster)集群。

                  什么是主从同步?

                  单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,可以搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据,主节点写入数据之后,需要把数据同步到从节点中。

                  JAVA面试八股文之Redis相关

                  主从同步数据的流程

                  主从同步分为了两个阶段,一个是全量同步,一个是增量同步。

                  全量同步是指从节点第一次与主节点建立连接的时候使用全量同步,流程是这样的:

                  第一:从节点请求主节点同步数据,其中从节点会携带自己的replication id和offset偏移量。

                  第二:主节点判断是否是第一次请求,主要判断的依据就是,主节点与从节点是否是同一个replication id,如果不是,就说明是第一次同步,那主节点就会把自己的replication id和offset发送给从节点,让从节点与主节点的信息保持一致。

                  第三:在同时主节点会执行bgsave,生成RDB文件后,发送给从节点去执行,从节点先把自己的数据清空,然后执行主节点发送过来的RDB文件,这样就保持了一致。

                  当然,如果在RDB生成执行期间,依然有请求到了主节点,而主节点会以命令的方式记录到缓冲区,缓冲区是一个日志文件,最后把这个日志文件发送给从节点,这样就能保证主节点与从节点完全一致了,后期再同步数据的时候,都是依赖于这个日志文件,这个就是全量同步。

                  JAVA面试八股文之Redis相关

                  增量同步指的是,当从节点服务重启之后,数据就不一致了,所以这个时候,从节点会请求主节点同步数据,主节点还是判断不是第一次请求,不是第一次就获取从节点的offset值,然后主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步。

                  JAVA面试八股文之Redis相关

                  怎么保证Redis的高并发高可用?

                  首先可以搭建主从集群,再加上使用redis中的哨兵模式,哨兵模式可以实现主从集群的自动故障恢复,里面就包含了对主从服务的监控、自动故障恢复、通知;如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主;同时Sentinel也充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端,所以一般项目都会采用哨兵的模式来保证redis的高并发高可用。

                  JAVA面试八股文之Redis相关

                  JAVA面试八股文之Redis相关

                  redis集群脑裂,该怎么解决呢?

                  有的时候由于网络等原因可能会出现脑裂的情况,就是说,由于redis master节点和redis salve节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到master,所以通过选举的方式提升了一个salve为master,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在old master那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将old master降为salve,这时再从新master同步数据,这会导致old master中的大量数据丢失。

                  解决方法,可以在redis的配置中可以设置:第一可以设置最少的salve节点个数,比如设置至少要有一个从节点才能同步数据,第二个可以设置主从数据复制和同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失。

                  redis的分片集群有什么作用?

                  分片集群主要解决的是,海量数据存储的问题,集群中有多个master,每个master保存不同数据,并且还可以给每个master设置多个slave节点,就可以继续增大集群的高并发能力。同时每个master之间通过ping监测彼此健康状态,就类似于哨兵模式了。当客户端请求可以访问集群任意节点,最终都会被转发到正确节点。

                  JAVA面试八股文之Redis相关

                  Redis分片集群中数据是怎么存储和读取的?

                  Redis 集群引入了哈希槽的概念,有 16384 个哈希槽,集群中每个主节点绑定了一定范围的哈希槽范围, key通过 CRC16 校验后对 16384 取模来决定放置哪个槽,通过槽找到对应的节点进行存储。取值的逻辑是一样的。

                  JAVA面试八股文之Redis相关

                  Redis是单线程的,但是为什么还那么快?

                  1、完全基于内存的,C语言编写。

                  2、采用单线程,避免不必要的上下文切换可竞争条件。

                  3、使用多路I/O复用模型,非阻塞IO。

                  例如:bgsave 和 bgrewriteaof 都是在后台执行操作,不影响主线程的正常使用,不会产生阻塞。

                  能解释一下I/O多路复用模型?

                  I/O多路复用是指利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。

                  其中Redis的网络模型就是使用I/O多路复用结合事件的处理器来应对多个Socket请求,比如,提供了连接应答处理器、命令回复处理器,命令请求处理器;

                  在Redis6.0之后,为了提升更好的性能,在命令回复处理器使用了多线程来处理回复事件,在命令请求处理器中,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程。

                  后记

                  👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]