Redis常见八股文
1.Redis五种数据基本类型
- 字符串String,适合存单一数据,
比如
用户的昵称 - 哈希 has,适合存对象,
比如
用户详情(name、age 这些字段),能单独改某个字段,不用动整一个对象 - 列表 list,有序可重复,适合做简单消息队列,
比如
注册通知,Lpush 存消息、Rpop 取消息 - 集合 set,无序不重复,适合去重或算交集,
比如
求两个用户的共同好友 - 有序集合 zset,按分数排序,最常用的是排行榜,
比如
商品销量榜
2.解释一下缓存穿透、击穿、雪崩
- 缓存穿透 一般是指查询一个不存在的数据,由于这个数据不存在,因此不写入缓存,所有的请求都打到数据库,导致数据库挂掉
(有两种解决方法,一种是缓存空指,另外一种是用布隆过滤器来进行过滤)
- 缓存击穿 是指对于一个设置了时间的key,缓存到某一个时间点过期,恰好这这个时间点对于这个key有大量的并发请求,请求发现缓存过期之后就会都请求到数据库,大量的请求可能会把数据库给压垮
(有两种解决方法,一种是设置永不过期,另外一种是添加一个互斥锁,第一个请求进来发现请求没有,其他的请求就会阻塞在外面,当第一个请求在数据库里面查到数据写入缓存之中,后面的请求再在缓存里面查询)
- 缓存雪崩 是指在大量的key在同一时间同时过期,大量的请求全部打到数据库里面,把数据库瞬间压垮
(解决方法:可以设置缓存的失效时间为随机值,避免大量的key同时失效)
3.Redis 的持久化
redis的持久化策略核心是两种,还有一个混合模式
- 一种是RDB,RDB是一种快照文件,它是把Redis内存存储的数据写到磁盘之中,是二进制文件,当Redis宕机想要恢复数据的时候,可以从RDB快照文件中来恢复数据。
优点
是它是二进制文件,保存的体积小,恢复的数据也快 - 另外一种是AOF,AOF的含义是追加文件,当Redis执行写的命令的时候,都会存储到这个文件中,当Redis宕机想要恢复数据的时候,会从这个文件中再次执行一次命令来恢复数据。
虽然
它相对于RDB来说速度慢一些,但是它丢失数据的风险会小很多
4.Redis的过期策略
主要有3种
- 第一种是定时删除,就是给过期键设个‘闹钟’,到期马上删。优点是内存释放及时,但如果过期键太多,‘闹钟’会占大量 CPU,实际很少用。
(主动)
- 第二种是惰性删除,过期了不主动删,等有人查这个键时再检查,过期就删。好处是省 CPU(不用主动扫),但缺点是如果过期键长期没人查,会一直占内存,可能内存泄漏。
(被动)
- 第三种是定期删除,每隔一段时间(比如 100ms),随机挑一批设置了过期时间的键,删掉其中已过期的。这样既不会像定时删除那样费 CPU,又比惰性删除更及时释放内存,是个折中方案。
(折中)
实际中 Redis 主要靠‘惰性删除 + 定期删除’配合:平时没人访问的过期键,靠定期扫描删一批;有人访问时,再通过惰性删除兜底,这样既控制了 CPU 消耗,又减少了内存浪费。”
5.Redis的数据淘汰策略
按 “淘汰范围” 分两类:
- 只淘汰 “设置了过期时间的键”(前缀volatile-):
适合 “部分数据有过期时间,且未设置过期时间的是核心数据(不能删)” 的场景。 - 所有键都可能被淘汰(前缀allkeys-):
适合 “所有数据都是缓存,允许淘汰任意键” 的场景(比如纯缓存服务)。
按 “淘汰逻辑” 分常见策略:
- LRU(最近最少使用
Least Recently Used
) 看时间距离:
volatile-lru
:从 “设置了过期时间的键” 中,淘汰 “最近最少被访问” 的键;
allkeys-lru
:从 “所有键” 中,淘汰 “最近最少被访问” 的键(缓存场景最常用,优先保留高频访问数据)。 - LFU(最近最少频率使用,
Least Frequently Used
Redis 4.0+ 看使用频率):
volatile-lfu/allkeys-lfu
:淘汰 “最近一段时间内访问频率最低” 的键(比 LRU 更精准,适合 “访问频率比访问时间更重要” 的场景,比如长期高频访问的热点数据)。 - TTL(剩余过期时间):
volatile-ttl
:从 “设置了过期时间的键” 中,淘汰 “剩余存活时间最短” 的键(适合 “优先保留存活久的临时数据” 场景)。 - 随机淘汰:
volatile-random/allkeys-random
:随机淘汰对应范围的键(很少用,除非对淘汰逻辑无特殊要求)。
不淘汰(默认): - noeviction:不淘汰任何键,此时再新增数据会返回错误(适合 “不允许丢失任何数据” 的场景,比如核心业务存储,需手动扩容避免触发)
6.Redis作为缓存,如何保证mysql的数据与Redis进行同步?
- 强一致性
使用读写锁,在读数据的时候添加共享锁,可以保证读读不互斥,读写互斥,在更新数据的时候添加排他锁,他是读读和读写都互斥,这样的话能保证在写数据的时候,不让其他线程来读数据,避免了脏数据 - 有一定延迟
使用Canal ,数据库执行写操作和更新操作的时候会记录到biglog文件中,Canal 本质是‘伪装成 MySQL 的从库’,通过读取 MySQL 的 binlog 日志获取数据变更,再把变更同步到 Redis
使用延迟双删,要执行写或者更新操作的时候,先删除缓存在操作数据库,等一段时间后再把数据库的数据同步到缓存之中
7.说一下Redis的分布式锁
“Redis 分布式锁的核心是解决‘分布式系统里多服务抢同一资源’的问题,比如秒杀时多个服务同时扣减库存,得保证同一时间只有一个服务能操作。
基本实现就两步:
抢锁:用SET命令的NX和EX参数 ——NX确保只有一个服务能抢到(互斥),EX给锁设个过期时间(比如 10 秒,防止服务宕机后锁一直占着,避免死锁)。而且 value 得用唯一标识(比如 UUID),后面释放锁时要验证是不是自己的锁。
放锁:不能直接删,得先查 value 是不是自己的,再删,这两步得原子操作(用 Lua 脚本),不然可能把别人的锁删了 —— 比如我持有的锁过期了,别人已经抢到新锁,我再删就错了。