温馨提示:距离2024年结束还剩18天,剩余约为4.92%...

转载

Redis的常见问题及其解答

1.简单介绍下redis?

  1. Redis是 C 编写的,高性能非关系型数据库。
  2. Redis 键只能为字符串,值支持五种数据类型:string、has、list、set、zset。
  3. Redis 的数据存在内存中的,读写速度非常快, redis 被广泛应用于缓存方向,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。
  4. 支持数据持久化,支持AOF(Append Only File)和RDB(Redis DataBase)两种持久化方式。
  5. 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
  6. Redis 也经常用来做分布式锁和分布式事务。
  7. 不能用作海量数据的高性能读写
  8. Redis 不具备自动容错和恢复功能。主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
  9. 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
  10. Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

简要回答:

  1. Redis是非关系型数据库
  2. 它的Value支持五种数据类型:String,List,Set,Zset,Hash。String和Hash使用最广泛。
  3. 数据存在内存中,用于缓存方向,超过 10万次/s读写操作。
  4. 支持AOF和RDB
  5. 支持主从复制。
  6. 可用来做分布式锁和分布式事务。
  7. 海量数据不适合,容错和恢复能力不太够,容易数据不一致,在线扩容难支持。

2.Redis缓存淘汰策略?

  1. Redis内存不足的缓存淘汰策略提供了8种。
    • noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键
    • allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键
    • volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键
    • allkeys-random:加入键的时候如果过限,从所有key随机删除
    • volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
    • volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
    • volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
    • allkeys-lfu:从所有键中驱逐使用频率最少的键
  2. 这八种大体上可以分为4中,lru、lfu、random、ttl。
    • lru:Least Recently Used),最近最少使用
    • lfu:Least Frequently Used,最不经常使用法
    • ttl:Time To Live,生存时间
    • random:随机
  3. 默认是noeviction。对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外
  4. eviction:“逐出;赶出;收回”。
  5. volatile:“不稳定的”。

3.Redis的过期键的删除策略?

  1. Redis中同时使用了惰性过期和定期过期两种过期策略。
  2. 常见有3种过期策略,定期过期,定时过期,惰性过期。
  3. 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
  4. 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。
  5. 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
  6. 定期、定时,是主动删除。惰性,是被动删除。

4.redis的缓存穿透问题?

  • 是什么?
    • 缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求
    • 关键字:都没有
    • 例子:如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
    • 缓存穿透和缓存击穿容易混,缓存穿透强调的是都没有。缓存击穿值得是缓存没有,数据库有,
  • 如何解决?常见3种方案
    • 增加接口层校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
    • key-null存入缓存,从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
    • 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。

5.redis的缓存击穿问题?

  • 是什么?
    • 缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
    • 关键字:缓存没有数据库有,并发多。
    • 例子:活动系统里面查询活动信息,但是在活动进行过程中活动缓存突然过期了。
    • 和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
    • 和缓存穿透不同的是,缓存穿透指缓存和数据库中都没有的数据。缓存击穿指的是缓存没有,数据库有
  • 解决方案
    • 设置热点数据永远不过期。简单来讲,就是不设置过期时间。来了就给你。
    • 加互斥锁(mutex key)。简单来讲,就是要操作db,需要排队。

辅助理解:

互斥锁的概念,共享资源的使用是互斥的,即一个线程获得资源的使用权后就会将该资源加锁,使用完后会将其解锁,如果在使用过程中有其他线程想要获取该资源的锁,那么它就会被阻塞陷入睡眠状态,直到该资源被解锁才会被唤醒,如果被阻塞的资源不止一个,那么它们都会被唤醒,但是获得资源使用权的是第一个被唤醒的线程,其它线程又陷入沉睡。 业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

 public String get(key) {
        String value = redis.get(key);
        if (value == null) { //代表缓存值过期
          //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
       if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  //代表设置成功
                value = db.get(key);
                       redis.set(key, value, expire_secs);
                       redis.del(key_mutex);
              } else {  //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
                       sleep(50);
                       get(key);  //重试
              }
        } else {
           return value;      
        }
  }

6.redis的缓存雪崩问题?

  • 是什么?
    • 在高并发下,大量的缓存key在同一时间失效,导致大量的请求落到数据库上,如活动系统里面同时进行着非常多的活动,但是在某个时间点所有的活动缓存全部过期。
    • 关键字:大量key,都失效。
    • 例子:如活动系统里面同时进行着非常多的活动,但是在某个时间点所有的活动缓存全部过期。
  • 怎么解决?
    • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
    • 一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
    • 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。

7.redis的持久化方式区别及其选择?

  1. 支持AOF(Append Only File)和RDB(Redis DataBase)两种持久化方式。
  2. RDB是默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
  3. AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。
  4. RDB,安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。
  5. AOF,安全性高。即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
  6. RDB,性能好,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。
  7. AOF比RDB更安全、更大,RDB比AOF性能更好。
  8. 可以同时两种方式,两个都配了优先加载AOF。AOF粒度更细
  9. 可以只使用RDB持久化,可以承受数分钟以内的数据丢失。
  10. 不推荐只使用AOF持久化,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug
  11. 不使用任何持久化方式,数据在服务器运行的时候存在。

8.Redis有哪些优缺点?

  1. 快,读的速度是110000次/s,写的速度是81000次/s
  2. 结构丰富,支持5种,分别是string、has、list、set、zset。
  3. 持久化,支持AOF(Append Only File)和RDB(Redis DataBase)
  4. 分布式事务,Redis的事务实质上是命令的集合,在一个事务中要么所有命令都被执行,要么所有事物都不执行。
  5. 分布式是锁,分布式锁是控制分布式系统之间同步访问共享资源的一种方式
  6. 主从复制,主机会自动将数据同步到从机,可以实现读写分离。
  7. 不能存海量数据,受到物理内存的限制
  8. 不具备自动容错和恢复功能,主机从机的 宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
  9. 可能数据不一致,主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
  10. Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂,为避免这一问题,上线时必须确保有足够的空间。

9.redis存储的数据类型有哪些?如何选择?

  1. STRING,字符串,最简单的k-v存储,短信验证码,配置信息等,就用这种类型来存储。
  2. HASH,包含键值对的无序散列表,一般key为ID或者其他唯一标识,value对应的就是详情了。如商品详情,个人信息详情,新闻详情等
  3. LIST,列表,因为list是有序的,比较适合存储一些有序且数据相对固定的数据。如省市区表、字典表。List还可以做消息队列。
  4. SET,无序集合,可以简单的理解为ID-List的模式,如微博中一个人有哪些好友,set最牛的地方在于,可以对两个set提供交集、并集、差集操作。例如:查找两个人共同的好友等。
  5. ZSET,有序集合,set的增强版本,增加了一个score参数,自动会根据score的值进行排序。比较适合类似于top N等不根据插入的时间来排序的数据。

10.redis的使用场景?

  1. 缓存,将热点数据放到内存中。
  2. 计数器,可以对 String 进行自增自减运算,从而实现计数器功能。
  3. 队列,List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息。不过最好使用 Kafka、RabbitMQ 等消息中间件。
  4. 分布式锁,在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
  5. 会话缓存,可以使用 Redis 来统一存储多台应用服务器的会话信息。
  6. 全页缓存(FPC),除基本的会话token之外,Redis还提供很简便的FPC平台。没接触过。
  7. 交集、差集、并集,Set 可以实现交集、差集、并集等操作,从而实现共同好友等功能。没接触过。
  8. 排行榜,ZSet 可以实现有序性操作,从而实现排行榜等功能。没接触过。
  9. 发布/订阅功能,用的少,没有MQ好。没接触过。

11.redis的如何做事务支持?

  1. Redis的事务实质上是命令的集合,在一个事务中要么所有命令都被执行,要么所有事物都不执行。
  2. 事务从开始到执行会经历以下三个阶段,MULTI 开始到 EXEC结束前,中间所有的命令都被加入到一个命令队列中;当执行 EXEC命令后,将QUEUE中所有的命令执行。也就是:
    • MULTI开始事务。
    • 命令入队列(QUEUE)。
    • EXEC触发执行事务。
  3. Redis的事务没有关系数据库事务提供的回滚(rollback),所以开发者必须在事务执行失败后进行后续的处理:
    • 如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
    • 如果在一个事务中出现运行错误,那么正确的命令会被执行。
  4. 此外我们可以使用DISCARD取消事务。

12.redis的缓存预热问题?

  1. 缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
  2. 解决方案:
    • 直接写个缓存刷新页面,上线时手工操作一下;
    • 数据量不大,可以在项目启动的时候自动进行加载;
    • 定时刷新缓存;
  • 作者:CZC(关于作者)
  • 发表时间:2024-07-05 15:06
  • 版权声明
  • 评论区:

    留言