Java面试——Redis专题

1、什么是缓存穿透?怎么解决?

缓存穿透:查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库。
解决方案一:缓存空数据
缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存
优点:简单
缺点:消耗内存,可能会发生不一致的问题
解决方案二:布隆过滤器
布隆过滤器主要是用于检索一个元素是否在一个集合中。我们使用的是redission实现的布隆过滤器。
它的底层主要是先去初始化一个bitmap,里面存放的二进制0或1。在一开始都是0,当一个key来了之后经过3次hash计算,模于数组长度找到数据的下标然后把数组中的原来的0改为1,这样的话3个数组的位置就能标明一个key的存在。查找过程也是一样的。
当然也是有缺点的,布隆过滤器可能会产生一定的误判,我们一般可以设置这个误判率,大概不会超过5%,其实这个误判是必然存在的,要不就得增加数组的长度。5%以内的误判率一般的项目也能接受,不至于高并发下压倒数据库。

2、什么是缓存击穿?怎么解决?

**缓存击穿:**给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发请求可能会瞬间吧数据库压垮。
解决方案一:互斥锁
可以使用redission的分布式锁实现,就是从redis中查询不到数据时,不要立刻查询数据库,而是先获取锁,获取锁后再去查询数据库,其他未获取到锁的请求进行重试,这样就可以确保只有一个查询数据库并且更新缓存的请求。
解决方案二:逻辑过期

  • 在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间
  • 当查询的时候,从redis取出数据后判断时间是否过期
  • 如果过期则开通另一个线程进行数据同步,当前线程正常返回过期数据

这两种方案各有利弊,如果选择数据的强一致性,建议使用分布式锁的方案,性能上可能没有那么高,锁需要等,也有可能产生死锁的问题。如果选择key的逻辑删除,则优先考虑高可用性,性能比较高,但是数据同步做不到强一致。

3、什么是缓存雪崩?怎么解决?

缓存雪崩一般由两种情况产生,第一种是由于大量的key设置了相同的过期时间,一旦到达过期时间点这些key集体失效,造成访问这些key的请求全部进入数据库,对数据库形成巨大压力。第二种是redis服务宕机,大量的请求进入数据库。
**解决方案一:设置不同的过期时间:**在过期时间上加上随机值
**解决方案二:利用redis集群提高服务高可用性:**比如redis的哨兵模式和集群模式
**解决方案三:给缓存业务添加降级限流策略:**使用nginx或者Spring Cloud Gateway限流
**解决方案四:给业务添加多级缓存:**使用Guava或者Caffeine实现多级缓存

4、Redis做为缓存,mysql的数据如何与Redis进行同步呢?(双写一致性)

(1)就说我最近做的这个项目,里面有xxxx(根据自己的简历)的功能,需要让数据库与redis高度保持一致,因为要求时效性比较高,我们当时采用的读写锁保证的强一致性
我们采用的是redisson实现的读写锁,在读的时候添加共享锁,可以保证读读不互斥,读写互斥。当我们更新数据的时候,添加排他锁,它是读写,读读都互斥,这样就能保证在写数据的同时是不会让其他线程读取数据的,避免了脏数据。这里面需要注意的是读方法和写方法上需要使用同一把锁才行。
那这个排他锁是如何保证读写、读读互斥的呢?
其实排他锁底层使用的也是setnx,保证了同时只能有一个线程操作锁的方法。
为什么不用延迟双删?
延迟双删,如果是写操作,我们先把缓存中的数据删除,然后更新数据库,最后在延迟删除缓存中的数据,其实这个延时多久不太好确定,在延时的过程中可能会出现脏数据,并不能保证强一致性,所以没有采用它。
(2)就说我最近做的这个项目,里面有xxxx(根据自己的简历)的功能,数据同步可以有一定的延时
我们当时采用的阿里的canal组件实现数据同步,不需要更改业务代码,部署一个canal服务。
canal服务把自己伪装成mysql的一个从节点,当mysql数据更新以后,cannal会读取binlog数据,然后通过cannal的客户端获取到数据,更新缓存即可。
其实还可以使用MQ中间件来发送异步通知,在mysql修改数据的时候,异步发送MQ消息到缓存服务,监听到消息后更新缓存也可

5、Redis延迟双删

什么是延迟双删?
延迟双删策略是分布式系统中数据库存储和缓存数据保持一致性的常用策略,但它不是强一致。其实不管哪种方案,都避免不了Redis存在脏数据的问题,只能减轻这个问题,要想彻底解决,得要用到同步锁和对应的业务逻辑层面解决。
为什么要进行延迟双删?
一般我们在更新数据库数据时,需要同步redis中缓存的数据,所以存在两种方法:
第一种方案:先执行update操作,再执行缓存清除。
第二种方案:先执行缓存清除,再执行update操作。
这两种方案的弊端是当存在并发请求时,很容易出现以下问题:
第一种方案:当请求1执行update操作后,还未来得及进行缓存清除,此时请求2查询到并使用了redis中的旧数据。
第二种方案:当请求1执行清除缓存后,还未进行update操作,此时请求2进行查询到了旧数据并写入了redis。
如何实现延迟双删?
所以此时我们需要使用第三种方案:先进行缓存清除,再执行update,最后(延迟N秒)再执行缓存清除。
需要注意的点
上述中(延迟N秒)的时间要大于一次写操作的时间,一般为3-5秒。
原因:如果延迟时间小于写入redis的时间,会导致请求1清除了缓存,但是请求2缓存还未写入的尴尬。。。
ps:一般写入的时间会远小于5秒
小结
延迟双删用比较简洁的方式实现 mysql 和 redis 数据最终一致性,但它不是强一致。
延迟,是因为 mysql 和 redis 主从节点数据同步不是实时的,所以需要等待一段时间,去增强它们的数据一致性。
延迟是指当前请求逻辑处理延时,而不是当前线程或进程睡眠延迟。

6、Redis作为缓存,数据的持久化是怎么做的?

在redis中提供了两种数据持久化的方式:RDB和AOF。
RDB是一个快照文件,它是把redis内存存储的数据写到磁盘上,当redis服务宕机回复数据的时候,方便从RDB快照文件中恢复数据。默认使用的是bgsave来保存快照数据。
优点:基于二进制文件完成数据备份,占用空间少,便于文件传输;
能够自定义规则,根据redis繁忙状态进行数据备份
缺点:无法保证数据完整性,会丢失最后一次快照后的所有数据;
bgsave每次执行都会阻塞redis服务进程创建子线程,频繁执行影响系统吞吐量
AOF是追加文件,当redis操作写命令的时候,都会存储这个文件中,当redis服务恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据。
AOF的触发机制有三种:分别为

  - always:每操作一条命令,都往AOF文件中同步一次,该方式效率最高,不会丢失数据,安全性最高,但是十分浪费资源
  - everysec:每次执行写入命令,先放到缓冲区,每隔一秒同步到AOF文件中,该方式兼备了效率和安全,即使出现服务宕机,也就丢失一秒钟的数据
  - no:将同步操作交给操作系统来做,该方式最快,但是最不安全

7、Redis的数据过期策略有哪些?(假如redis的key过期后会立即删除吗)

在redis中提供了两种数据过期策略:
第一种是惰性删除,在设置该key的过期时间后,我们不去管它,当需要该key时,我们检查其是否过期,如果过期,我们就删除掉它,反之返回该key。
第二种是定期删除,每隔一段时间,我们就对一些key进行检查,删除里面过期的key
定期删除有两种模式:

  - SLOW模式是定时任务,执行频率默认为10hz(每秒执行10次),每次不超过25ms,可以通过修改配置文件redis.conf的hz选项来调整这个次数
  - FAST模式执行频率不固定,但每次间隔不低于2ms,每次耗时不超过1ms

redis的过期删除策略:惰性删除+定期删除两种策略配合使用

8、Redis的数据淘汰策略(假如缓存过多,内存是有限,内存被占满了怎么办)

嗯,这个在redis中提供了很多种,默认是noeviction,不删除任何数据,内存不足直接报错。是可以在redis的配置文件中进行设置的,里面有两个非常重要的概念,一个是LRU,一个是LFU。
LRU的意思是最近最少使用,用当前时间减去最后一次访问的时间,这个值越大则淘汰优先级越高。
LFU的意思是最少频率使用,它会统计每个key的访问频率,值越小淘汰优先级越高。
我们在项目中设置的是allkeys-lru,挑选最近最少使用的数据淘汰,把一些经常访问的key留在redis中。

9、Redis有哪些集群方案?

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

10、Redis主从同步

是这样的,单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,可以搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据,主节点写入数据后,需要把数据同步到从节点中。
主从同步分为两个阶段,一个是全量同步,一个是增量同步。
全量同步是指从节点第一次与主节点建立连接的时候使用全量同步,流程是这样的:
第一:从节点请求主节点同步数据,其中从节点会携带自己的replication id和offset偏移量。
第二:主节点判断是否是第一次请求,主要判断依据就是,主节点与从节点是否是同一个replication id,如果不是,就说明是第一次同步,那主节点就会把自己的replication id和offset发送给从节点,让从节点与主节点的信息保持一致。
第三:在同时主节点会执行bgsave,生成rdb文件后,发送给从节点去执行,从节点先把自己的数据清空,然后执行主节点发送过来的rdb文件,这样就保持了一致。
当然,如果在rdb生成执行期间,依然有请求到了主节点,而主节点会以命令的方式记录到缓冲区,缓冲区是一个日志文件,最后把这个日志文件发送给从节点,这样就能保证主节点与从节点完全一致了,后期再同步数据的时候,都是依赖于这个日志文件,这个就是全量同步。
增量同步指的是,当从节点服务重启后,数据就不一致了,所以这个时候,从节点会请求主节点同步数据,主节点还是判断是不是第一次请求,不是第一次就获取从节点的offset值,然后主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步。

11、怎么保证Redis的高并发高可用(哨兵模式)

首先可以搭建主从集群,再加上使用Redis中的哨兵模式,哨兵节点会监控主节点和从节点的运行状态,如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主;同时Sentinel也充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端,所以一般项目都会采用哨兵模式来保证Redis的高并发高可用。
优点:保证Redis的高可用性,避免了因单节点故障导致服务不可用的情况。
哨兵节点之间相互独立,可以提高系统的可靠性和稳定性。
配置简单,易于拓展。
缺点:哨兵节点不能自动进行故障恢复,只能进行故障转移,需要人工介入进行故障恢复操作。
在进行故障转移时,会存在短暂的数据不一致现象。
哨兵节点本身也可能成为系统的单点故障,需要进行高可用部署。

12、Redis分片集群模式

分片集群模式是将数据分散到多个节点上,以提高系统吞吐量和可用性。其中,每个节点负责一部分数据,称为分片,客户端通过路由算法将操作发送到相应的节点。
优点:可以水平扩展,增加节点数可以增加系统容量。
单个节点故障不会影响整个集群。
分布式存储可以提高系统的可靠性和可用性。
缺点:集群管理比较复杂,需要考虑节点间的路由,负载均衡,数据迁移等问题。
集群分布不均匀时可能导致某些节点热点数据过多,从而影响性能。
事务操作有限制,只支持的单个节点内的事务,跨节点事务需要手动实现。
总体来说,Redis的分片集群模式适合于需要处理海量数据的场景,并且要求高可用和可伸缩性,但也需要在架构设计和节点管理上进行充分考虑和规划。
image.png

13、Redis为什么被设计成单线程?

Redis被设计成单线程的原因是为了最大限度地利用CPU缓存和避免多线程并发带来的锁争用、上下文切换等开销,从而实现更高的性能和更低的延迟。此外,Redis采用异步I/O模型,可以同时处理多个客户端请求,进一步提高了并发能力。

14、Redis为什么那么快?

  1. 内存存储:Redis将所有数据存储在内存中,这使得它能够快速地读写数据。与传统的磁盘存储相比,内存存储的访问速度更快。
  2. 单线程架构:Redis是单线程的,这意味着Redis不需要进行上下文切换,也不需要考虑线程同步和加锁等问题,从而避免了这些常见的多线程问题带来的开销。
  3. 异步非阻塞I/O:Redis使用了事件驱动机制,并采用了异步非阻塞I/O模型,这使得Redis能够处理大量的并发连接请求,提高了系统的吞吐量。
  4. 基于内存的操作:Redis的所有操作都是基于内存的,这使得Redis能够在微秒级别完成大部分操作,从而提高了系统的响应速度。

15、Redis的脑裂问题是什么?怎么解决?

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

  1. 使用sentinel来监控和自动故障转移。sentinel可以在主节点出现故障时自动将从节点提升为新的主节点,并将客户端重定向到新的主节点。
  2. 使用cluster来分片和复制数据。如果一个节点失效,cluster会重新分配槽位,并且客户端可以自动通过其他节点访问集群的其他部分。
  3. 配置Redis的持久化,如AOF或RDB,以确保在故障发生时可以快速恢复数据。
  4. 配置合适的超时时间,以避免网络分区过长导致误判节点状态,并定期检查集群健康状况。

16、I/O多路复用模型?

image.png

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis是一款高性能的内存键值数据库,它具有快速读写速度和高可靠性。在面试中,Java开发人员与Redis缓存相关的问题通常涉及如何在Java应用程序中实现Redis缓存来提高性能。 首先,Java开发人员应该了解与Redis交互的客户端API,例如Jedis和Lettuce。这些API允许Java应用程序通过TCP连接与Redis服务器进行交互,并提供了基本的高级数据类型操作和事务处理机制。开发人员应该熟练掌握这些API并了解它们的性能优化策略。 其次,Java开发人员应该知道如何在应用程序中使用 Redis缓存来缓存常见的数据,例如数据库查询结果或API响应数据。这可以通过将数据存储在Redis中,以便下次访问时可以更快地读取数据来实现。开发人员应该考虑如何选择合适的数据结构(例如哈希表或有序集合)来保存数据。 另外,Java开发人员应该了解Redis缓存的通用最佳实践策略,例如如何配置Redis服务器以优化性能,如何进行数据备份和恢复,以及如何监视和调试Redis性能和健康状况。 最后,Java开发人员应该知道如何与Redis缓存集成来支持应用程序的高可用性和可伸缩性。例如,他们可以使用Redis Sentinel来自动监视Redis集群的健康状况,以及Redis Cluster来实现数据分片和负载均衡。 总之,在Java面试中,了解Redis缓存的基本知识和通用最佳实践策略非常重要,因为它可以让开发人员设计和实现高性能,可伸缩和高可用性的Java应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值