1.为什么使用缓存?
mysql:关系型数据库,基于磁盘存储
redis:非关系型数据库;基于内存存储;读写速度快,极大地提高应用的响应速度和吞吐量。
2 使用缓存会带来哪些问题?
- 数据不一致:指缓存中的数据与数据库中的数据不一致
✅更新数据库后,缓存未更新:导致读取到旧数据。
✅缓存更新成功,数据库更新失败:缓存中有新数据,数据库中是旧数据。
✅并发更新:多个操作同时更新同一数据,导致数据不一致。
2.缓存穿透:大量请求查询不存在的数据,导致请求直接打到数据库。✅请求的key在缓存和数据库中都不存在。
✅恶意攻击者可能使用大量不存在的key进行查询。
解决方案缓存空数据:查询返回的数据为空,仍把这个空结果进行缓存
布隆过滤器:检查一个元素是否在一个集合中,拦截不存在的数据
3.缓存击穿:某个热点key在过期瞬间,大量请求同时访问,导致请求直接打到数据库,导致瞬间压力过大
解决方案添加互斥锁:一个线程查询未命中,获取互斥锁后查询数据库重建缓存数据,其他线程获取锁失败只能休眠一会重试
逻辑过期:
4.缓存雪崩:由于大量缓存同时过期或Redis宕机,导致数据库面临巨大访问压力
解决方案给不同key的TTL添加随机值:尽量不让key同时过期
利用redis集群提高服务的可用性:防止宕机
给缓存业务添加降级限流策略:ngxin等当中设置限流规则
给业务添加多级缓存:预防大量key过期
3 redis为什么这么快?
- 内存存储:所有数据存储在内存中,内存的读写速度远快于磁盘
- 单线程模型:在任何时刻只有一个命令在执行,避免了线程切换和锁竞争带来的消耗。
⚠️:Redis6.0+:多线程只用于网络IO,命令执行仍然是单线程
单线程运行快的原因:避免多线程带来的开销。多线程需要切换上下文,还可能有锁竞争的问题,都会消耗额外的资源。- 高效的数据结构-散列表,通过key去查找value String字符串、 Hash字典、 List列表、 Set集合、 zSet有序集合
- I/O 多路复用 (如 epoll、kqueue、select)来同时监听多个 socket 的读写事件。当某个 socket 有事件发生时,Redis 会处理该事件,这样就能使用一个线程处理大量的并发连接。
4. redis数据结构的底层实现
string
list 底层是压缩列表(ziplist)
当列表较长时,会变成双向链表(linkedlist)
Hash 内容少,用ziplist 内容多,底层是哈希表(hash table)。使用拉链法避免冲突;redis obj会有两个map结构:ht0和ht1。一般只用到一个,当需要扩容和缩容的时候,才会用到另外一个。
set 数据少的时候使用整数集合(intset) 数据多,使用哈希表
sorted 数据少 ziplist 数据多 跳表(skip list)可参考
5.redis数据类型使用场景
- string字符串。
缓存功能、计数、共享session、限速- hash键值对,key是字符串,value是Map集合。比如value = {name: '沉默王二', age: 18},name 和 age 属于字段 field,沉默王二 和 18 属于值 value。
缓存用户信息、缓存对象- list字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。
消息队列、文章列表- set集合-字符串的无序集合,集合中元素唯一。
标签(tag)、共同关注- sorted set 有序集合,比 set 多了一个排序属性 score(分值)
用户点赞统计、用户排序
6.持久化
6.1 为什么要持久化
redis是基于内存存储的,速度快但易失数据。为确保数据在突发事件中不会丢失。Redis提供了三种持久化机制:RDB、AOF和混合持久化。
6.2 RDB 二进制快照持久化
在指定的时间间隔内,将内存中的数据集快照写进磁盘。
可通过 save 和 bgsave 命令两个命令来手动触发 RDB 持久化操作
save 命令:会同步 地将 Redis 的所有数据保存到磁盘上的一个 RDB 文件中。这个操作会阻塞所有客户端请求直到 RDB文件被完全写入磁盘。当 Redis 数据集较大时,使用 SAVE 命令会导致 Redis 服务器停止响应客户端的请求 。
不推荐在生产环境中使用,除非数据集非常小,或者可以接受服务暂时的不可用状态。
bgsave 命令:会在后台异步 地创建 Redis 的数据快照,并将快照保存到磁盘上的 RDB 文件中。这个命令会立即返回,Redis 服务器可以继续处理客户端请求。在 BGSAVE 命令执行期间,Redis 会继续响应客户端的请求,对服务的可用性影响较小。快照的创建过程是由一个子进程完成的,主进程不会被阻塞。是在生产环境中执行 RDB 持久化的推荐方式。
6.3 AOF 日志持久化
当 AOF 持久化功能被启用时,Redis 服务器会将接收到的所有写命令(比如 SET, LPUSH, SADD 等修改数据的命令)追加 到 AOF 缓冲区(buffer)的末尾。
always :每次写命令都会同步到 AOF 文件,这提供了最高的数据安全性,但可能因为磁盘 I/O 的延迟而影响性能。
everysec (默认):每秒同步一次,这是一种折衷方案,提供了较好的性能和数据安全性。如果系统崩溃,最多可能丢失最后一秒的数据。
no:只会在 AOF 关闭或 Redis 关闭时执行, 或由操作系统内核触发。在这种模式下,如果发生宕机,那么丢失的数据量由操作系统内核的缓存冲洗策略决定。
6.4 混合持久化 RDB+AOF(redis4.0+)
7.redis到底是单线程还是多线程
redis 6.0版本之前的单线程是指:其网络I/O和键值对读写是由一个线程完成的
redis 6.0引入的多线程是指:网络请求过程采用了多线程,而键值对读写命令仍然是单线程处理
8. 布隆过滤器是如何解决缓存穿透的?有何优缺点?
布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,用于快速检查一个元素是否存在于一个集合中。布隆过滤器由一个长度为 m 的位数组和 k 个哈希函数组成。
但是布隆过滤器也有一定的缺点,因为是通过哈希函数计算的,所以存在哈希冲突的问题,可能会导致误判。
9. redis与mysql双写一致性
9.1 读取数据逻辑
1.尝试读取缓存,有数据--返回
2.没有数据--查询mysql数据库并将返回数据,同时将数据存储在redis中,设置过期时间(避免占用redis内存
9.2 写数据流程
延迟双删:删除缓存;在修改数据库;(延迟一会);删除缓存
10. redis过期键删除策略
- 惰性删除:当访问key时,才判断key是否过期,过期则删除
优点:对CPU友好,用不到的key不用浪费时间检查
缺点:对内存不友好,一个key已经过期,但一直没有使用,该key就会一直在内存中永远不释放- 定期删除:每隔一段时间,对key进行检查,删除里面过期的key(从一定数量的数据库取一定量的key)
优点:可以通过限制删除频率和时长减少对CPU影响
缺点:难以确定频率和时长
11. redis缓存淘汰算法
- 先进先出FIFO:根据缓存存储时间,离当前最远的数据优先被淘汰
- 最近最少使用LRU:根据最近被使用的时间,离当前最远的数据优先被淘汰
- 最不经常使用LFU:在一段时间内,缓存数据被使用次数最少的优先淘汰
12. redis主从复制
主从复制:将一台redis服务器的数据,复制到其他的redis服务器。前者称为主节点(master),后者称为从节点(slave)。
数据的复制是单向的,只能从主节点到从节点。一个数据库可以有多个从节点,但是只能有一个主节点。
Redis主从复制分为全量复制和增量复制。
- 全量复制
当从节点第一次连接主节点时,会触发全量复制。
主节点会生成一个RDB快照文件发送给从节点,同时将期间的写命令缓存在内存中;等RDB文件发送完成后,再发送缓存中的写命令给从节点。
从节点先清空自己的旧数据,然后加载RDB文件,再执行缓存的写命令。- 增量复制
当主从节点之间网络断开重连后,如果条件允许,主节点会将断开期间收到的写命令发送给从节点,从而保持数据一致。
过程原理

13. redis哨兵模式
主从复制存在一个问题,没法完成自动故障转移。哨兵就可以解决。
它由两部分组成:哨兵节点和数据节点
- 哨兵节点: 哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的 Redis 节点,不存储数据,对数据节点进行监控
- 数据节点: 主节点和从节点都是数据节点
哨兵功能
- 集群监控:负责监控redis的主从进程是否正常运行
- 消息通知:某个redis实例有故障,哨兵负责发送信息作为报警 通知给管理员
- 故障转移:主节点挂了,自动转移到从节点上
- 配置中心:发生故障转移,通知客户端新的主节点地址
哨兵模式实现原理
- 监控:
每个 Sentinel 以 1s/次 的频率向它所监控的主节点、从节点以及其他 Sentinel 实例发送 PING 命令。- 主观下线和客观下线
当这些节点超过 down-after-milliseconds 没有进行有效回复,Sentinel 会将该实例标记为主观下线
当 Sentinel 将一个主节点判断为主观下线后,它会向其他 Sentinel询问,看它们是否也认为该主节点主观下线。当足够数量的 Sentinel 在指定时间范围内都报告主节点主观下线,那么该主节点会被标记为客观下线- 选举领导者 Sentinel
客观下线之后,Sentinel 通过 Raft 算法选举一个领导者 Sentinel 来进行故障转移。- 故障转移
在已下线主节点的从节点中,挑选一个作为新的主节点。挑选规则:优先级高and复制偏移量大
14. redis集群(Cluster)
- 数据分区
采用 哈希槽(Hash Slot) 进行数据分布,无需手动分片。redis cluster 有16384个槽。
每个键通过哈希算法(CRC16)映射到这些槽上,每个集群节点负责管理一定范围内的槽。- 高可用
集群支持主从复制和主节点的 自动故障转移 (与哨兵类似),当任一节点发生故障时,集群仍然可以对外提供服务。
15. redis集群方案
主从复制、哨兵模式、集群
16.redis分布式锁
分布式锁需要满足:
- 互斥性 - 同一时刻只有一个客户端能持有锁
- 防死锁 - 锁必须有超时机制,避免持有者崩溃后锁无法释放
- 安全性 - 只能由加锁者自己解锁(防误删)
- 高可用 - Redis 集群故障时仍能正常工作
实现方案
- SET NX EX(推荐
bash
SET lock_key unique_value NX EX 10
- SETNX + EXPIRE(不推荐
bash
SETNX lock_key unique_value
EXPIRE lock_key 10
问题与解决
- 锁提前过期,业务还在执行(看门狗机制,自动续期)
- 加锁和释放锁不是同一个线程的问题(在value中存入uuid-线程唯一标识。删除锁时判断该标识,使用lua保证原子性)
- 主从切换导致锁丢失(使用redLock解决)
17. 热key
在很短时间内被频繁访问的键。比如,热门新闻或热门商品。
解决方案:
- 本地缓存(多级缓存)
将热key缓存在应用服务器的本地内存中(如Guava Cache、Caffeine),减少对Redis的访问。- 热key拆分
将一个热key拆分为多个key,分散到不同节点。- 读写分离
主节点处理写请求
多个从节点处理读请求,分摊读压力
18.大key
- 大 key 指的是存储了大量数据的键,比如:单个简单的 key 存储的 value 很大,size 超过 10KB
hash,set,zset,list 中存储过多的元素(以万为单位)- 大 key 会造成什么问题呢?
客户端耗时增加,甚至超时
对大 key 进行 IO 操作时,会严重占用带宽和 CPU,造成 Redis 集群中数据倾斜
主动删除、被动删等,可能会导致阻塞- 大 Key 的检测
使用 redis-cli --bigkeys 命令,可以统计最大 key。
使用第三方工具,如 redis-rdb-tools 分析 RDB 文件。- 大 Key 的处理
删除:使用 UNLINK 命令异步删除大 key,避免阻塞。
拆分:将大 key 拆分为多个小 key。
压缩:使用压缩算法压缩 value,但会增加 CPU 开销。
19. redis事务机制
redis中事务是一组命令的集合,只保证了正常情况下的原子性-要么全部执行,要么全部不执行;一般不提供回滚机制。
所有命令在执行之前都会被放入一个队列中,直到执行EXEC命令时,所有命令才会按顺序执行。
主要通过 multi、exec、discard、watch等命令来实现:
- multi:标记一个事务的开始 ,,所有的命令都会被放入一个队列中,而不是立即执行。
- exec:按顺序执行执行所有放入队列的命令 。不支持回滚机制,如果在事务执行过程中发生错误,已经执行的命令不会被回滚。
- discard:取消事务,放弃执行事务块内的所有命令
- watch:监视一个或多个 key,如果在事务执行之前这个 key 被其他命令所改动,那么事务将被打断
20.什么情况下redis哨兵模式会产生数据丢失?
- 主从复制的延迟,比如主节点刚接收到新数据,还没来得及同步给从节点,就突然宕机了,那么这部分没同步的数据就丢了。
- 脑裂问题,主节点和哨兵集群之间的网络断了,哨兵误以为主节点宕机,就会选举一个新主节点。这时候旧主节点可能还在运行,接收客户端的写请求,等网络恢复后,旧主节点会被降为从节点,它上面那些没同步到新主节点的数据,就会被覆盖掉,从而导致丢失。
21. redis中的管道有什么用?
Redis中的管道(Pipeline)主要用于批量执行多个命令,用来提高性能和效率。
管道可以将多个命令一次性发给服务器,然后返回多个结果。
22. 看门狗机制的原理是什么?
Redisson 的看门狗机制是一种用于自动续约分布式锁的机制,确保在持有锁的客户端处理完业务逻辑之前,锁不会过期。比如,我们平时使用分布式锁的时候,一般会设置一个锁的过期时间,那么如果锁过期的时候,业务还没执行完怎么办,于是就有了看门狗。
原理
- 初始锁定:
当客户端获取到锁时,会在 Redis 中设置一个键(代表锁)和一个过期时间(默认30秒)。同时,Redisson 会启动一个后台任务(看门狗),这个任务会定期检查锁的状态。- 自动续约:
看门狗任务会每隔一段时间(默认是锁的过期时间的1/3,即10秒)检查锁的状态。如果锁仍然被持有(即客户端还在持有锁且没有释放),看门狗任务会将锁的过期时间重置为初始值(例如,再次设置为30秒)。这样,锁的过期时间不断被延长,直到客户端明确释放锁或者客户端挂掉。- 释放锁:
当客户端完成业务逻辑后,会显式地调用unlock()方法释放锁。一旦锁被释放,看门狗任务会停止续约,锁在 Redis 中的键会被删除或自然过期。
23. 本地缓存与分布式缓存的区别?
| 本地缓存 cacffine/guava cache | redis | |
|---|---|---|
| 速度 | 快 | 可能会增加额外的网络开销,导致访问速度略低于本地缓存。 |
| 存储 | 存储在一个节点中,多个应用实例之间无法共享缓存数据。数据随应用进程的重启而丢失。 | 通过将数据分片存储在多个节点上,提高了缓存的容量和可扩展性。部分数据会被复制到多个节点上,以提高数据的可靠性和可用性 |
| 容量 | 容量受到内存大小的限制,一旦超过容量限制,可能会导致性能下降或者数据丢失。无法动态扩展。 | 根据需求动态添加和删除节点,以适应数据量的变化和访问负载的增加。redis 水平扩展。 |
当并发巨大的时候,如果 redis 的网络和 cpu 成为了瓶颈,一般可以增加一层本地缓存来进行缓冲。也就是我们说的多级缓存。



