JAVA常见面试题总结(八)Redis

    技术2022-07-14  87

    文章目录

    1、Redis 是什么?都有哪些使用场景?2、Redis 和 Memcached 有什么区别?3、Redis为什么是单线程的?4、说一说缓存穿透、缓存击穿、缓存雪崩,以及解决方案。5、Redis的数据类型有哪些?6、Redis 支持的Java客户端有哪些?7、Jedis 和 Redisson 有哪些区别?8、怎么保证缓存和数据库数据的一致性?9、Redis 持久化有几种方式?10、Redis的分布式锁怎么实现?11、Redis 如何做内存优化?12、Redis 的淘汰策略有哪些?13、Redis 常见的性能问题有哪些?该怎么解决?

    1、Redis 是什么?都有哪些使用场景?

    Redis(Remote Dictionary Server) 是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

    Reids 使用场景:

    热点数据的缓存限时业务的运用,例:手机验证码、限时优惠活动信息计数器相关问题,例:限制接口请求次数,短信发送数量等排行榜相关问题分布式锁消息队列

    2、Redis 和 Memcached 有什么区别?

    Memcached只有一种数据类型:string。Redi则支持更多的数据类型:string、list、hash、set、zset。Redis的速度比Memcached快很多。Redis可以持久化其数据。

    3、Redis为什么是单线程的?

    Redis的操作都是在内存中进行的,由于内存的速度快,CPU也并不是其性能瓶颈,所以使用单线程以减少设计的复杂度。

    注: Redis的6.0版本引入了多线程以解决读写网络的read/write操作占用了太多的CPU时间,减少多主线程的阻塞时间,多线程只是处理读写网络的read/write操作,执行命令的依然是单线程。

    4、说一说缓存穿透、缓存击穿、缓存雪崩,以及解决方案。

    缓存穿透:频繁查询一个Redis和数据库都不存在的数据,很有可能是恶意攻击,会对数据库产生很大的压力。

    解决方案:

    接口增加用户权限校验,id做校验,例如 id<0 的直接拦截。在数据库中取不到的数据存一个空值到redis,设置缓存有效时间较短,例如30秒后过期。使用布隆过滤器,布隆过滤器可以做到的效果:一个一定不存在的数据会被拦截掉,可能存在的数据才会去查数据库。因为这个特性,只要设置合理的参数就可以极大的缓解缓存穿透问题。

    缓存击穿:一条热点数据的缓存过期了,导致一瞬间大量请求打到了数据库。

    解决方案:

    热点数据的缓存永不过期(这个在实际场景中很少遇到,很多场景热点数据变化很快,而且不容易确定)

    加互斥锁,且不能使用synchronized,要做到获取不到锁不自动等待锁,所以可以用 ReentrantLock 的tryLock 加锁,获取不到锁就返回失败,自己控制等待一段时间再获取数据。

    注意: 此方式不适合分布式系统,分布式系统需要将 ReentrantLock 换成分布式锁。

    public String getData(String key) throws InterruptedException { //从缓存获取数据 String result = getDataByRedis(key); //从缓存中取不到数据 if (result == null){ //获取锁,获取成功去数据据取数据 if (reentrantLock.tryLock()){ //从数据库取数据 result = getDataByMySQL(key); if (result != null){ //更新缓存数据 setDateToRedis(key, result); } reentrantLock.unlock(); }else { //未获取到锁,等待100毫秒再重新获取数据 Thread.sleep(100); result = getData(key); } } return result; }

    缓存雪崩:一批数据同时过期了,导致大量的请求打到了数据库。

    解决方案:

    缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。如果遇到必须要同时过期的情况,例如每日固定时间更新排行榜数据,无法分散过期时间,这时候可以从业务层考虑,service层查询数据的时候设置个随机延迟时间,这样一部分请求访问过数据库后将数据存入缓存,另一部分请求再访问就能命中缓存了。

    5、Redis的数据类型有哪些?

    string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(sorted set:有序集合)。

    6、Redis 支持的Java客户端有哪些?

    Jedis、Redisson、Lettuce等。Redis官方推荐使用Redisson。

    7、Jedis 和 Redisson 有哪些区别?

    Jedis 是老牌的 Redis 实现客户端,提供了较全的 Redis 命令支持。

    Redisson 实现了分布式和可扩展的Java数据结构,和 Jedis 相比不支持 Redis 的一些特性,促使使用者对 Redis 的关注分离,从而让使用者更加关注于业务本身。

    8、怎么保证缓存和数据库数据的一致性?

    合理设置缓存数据的过期时间。新增、修改、删除数据库数据的同时修改缓存数据,可以使用事务机制来保证数据的一致性。

    9、Redis 持久化有几种方式?

    Redis 有两种方式对数据进行持久化。

    RDB(Redis Database):指定时间间隔对数据进行快照保存。AOF(Append Only File):将对数据的修改操作追加到日志文件中,重启 Redis 后通过解析日志恢复数据。Redis中AOF有三种同步方式,分别是每秒同步,每次操作同步,不同步。

    10、Redis的分布式锁怎么实现?

    Redis 分布式锁的实现方式其实就是通过一个原子性操作(setnx命令)往 Redis 存一条数据(占一个“坑“),且此数据不存在才能存入成功(抢到了”坑“位),如果数据已经存在就不能存入此数据(没抢到”坑“位)。

    只有占”坑“成功了才说明拿到了锁,可以继续执行后续操作,未占”坑“成功的则放弃或稍后重试。

    拿到锁的程序在执行完操作后要及时释放锁(让出”坑“位)。

    这里有一个问题,如果占”坑“成功的程序挂掉了怎么办?不主动释放锁会导致锁一直存在,后续程序一直等待获取锁,所以一般的解决方式是给锁加一个过期时间,防止因意外情况导致锁无法释放。

    这里会出现新的问题,如果在锁的有效期内程序未能执行完操作怎么办?此时就需要一个程序监控锁的状态,如果锁快到期了任务还没执行完,就要重置锁的过期时间(给锁续期)。

    11、Redis 如何做内存优化?

    尽量选用hash表存储数据,减少key的数量。选择合适的淘汰策略。

    12、Redis 的淘汰策略有哪些?

    volatile-lru:从设置过期时间的数据集(server.db[i].expires)中挑选出最近最少使用的数据淘汰。没有设置过期时间的key不会被淘汰,这样就可以在增加内存空间的同时保证需要持久化的数据不会丢失。volatile-ttl:从设置过期时间的数据集(server.db[i].expires)中挑选出将要过期的数据淘汰。volaile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰,该策略要淘汰的key面向全体key,而非只是设置过期时间的key。no-enviction:禁止驱逐数据,当内存不足以容纳新数据时,新写入数据就会报错,此策略是系统默认的一种淘汰策略。

    13、Redis 常见的性能问题有哪些?该怎么解决?

    Master 写内存快照,此操作会阻塞主线程工作,当快照比较大时对性能的影响会很大,有可能会导致短暂性的暂停服务。

    Master 启用AOF持久化,当AOF文件过大时,影响Redis 的重启速度。

    Master 主从复制的问题,Redis的主从复制,第一次Slave向Master请求同步时,Master会先建立内存快照,然后全量传输给Slave,然后再将缓存的命令发送给Slave,初次同步完成以后,后面的同步就是将变量的快照依次发送给Slave。

    解决方法:

    Master不要启用持久化,选一台Slave启用持久化。Master和Slave最好在同一个局域网内。避免在压力较大的Master上增加Slave。不使用图状结构使用链式结构,即:Master -> Slave1 -> Slave2 -> Slave3…
    Processed: 0.011, SQL: 9