java面试之必问的redis
redis 作为 中间件 乃是最常用技术, 缓存 做消息队列 或者利用redis的数据结果做一些限制都很好用。下面分享一下我学习到的redis相关问题。
为什么要用 Redis?
1、访问速度更快
传统数据库数据保存在磁盘,而 Redis 基于内存,内存的访问速度比磁盘快很多。引入 Redis 之后,我们可以把一些高频访问的数据放到 Redis 中,这样下次就可以直接从内存中读取,速度可以提升几十倍甚至上百倍。
2、高并发
一般像 MySQL 这类的数据库的 QPS 大概都在 4k 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 5w+,甚至能达到 10w+(就单机 Redis 的情况,Redis 集群的话会更高)。
QPS(Query Per Second):服务器每秒可以执行的查询次数;
由此可见,直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。进而,我们也就提高了系统整体的并发。
3、功能全面
Redis 除了可以用作缓存之外,还可以用于分布式锁、限流、消息队列、延时队列等场景,功能强大!
什么是 Redis Module?有什么用?
目前,被 Redis 官方推荐的 Module 有:
RediSearch:用于实现搜索引擎的模块。
RedisJSON:用于处理 JSON 数据的模块。
RedisGraph:用于实现图形数据库的模块。
RedisTimeSeries:用于处理时间序列数据的模块。
RedisBloom:用于实现布隆过滤器的模块。
RedisAI:用于执行深度学习/机器学习模型并管理其数据的模块。
RedisCell:用于实现分布式限流的模块
Redis 为什么这么快?
Redis 内部做了非常多的性能优化,比较重要的有下面 3 点:
Redis 基于内存,内存的访问速度比磁盘快很多;
Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用(Redis 线程模式后面会详细介绍到);
Redis 内置了多种优化过后的数据类型/结构实现,性能非常高。
Redis 通信协议实现简单且解析高效。
除了 Redis,你还知道其他分布式缓存方案吗?
比较老牌的我知道有一个memcached。
我关注到腾讯有一个Tedis:
Redis的基本数据类型有哪些?
Redis的基本数据类型有哪些?
Redis 的使用场景
Redis怎么实现的io多路复用
因为 Redis 是跑在「单线程」中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入 或 输出都是阻塞的,所以 I/0 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/0 阻塞导,致整个进程无法对其它客户提供服务。而 I/0 多路复用就是为了解决这个问题而出现的。为了让单线程(进程)的服务端应用同时处理多个客户端的事件,Redis 采用了 I0 多路复用机制。
这里“多路“指的是多个网络连接客户端,“复用”指的是复用同一个线程(单进程)。I/0 多路复用其实是使用个线程来检查多个 Socket 的就绪状态,在单个线程中通过记录跟踪每一个 socket(I/0流)的状态来管理处理多个 I/O 流。如下图是 Redis 的 I/O 多路复用模型:
缓存雪崩和缓存击穿
Redis缓存雪崩是指在高并发情况下,缓存中的大量数据在短时间内失效(或者被清除),导致大量请求直接访问数据库,从而引发数据库的访问压力急剧增加,可能导致数据库崩溃,进而影响整个系统的稳定性和性能。
这种现象的主要原因是,缓存中的数据有可能在同一时间段集中过期,导致大量的请求直接打到数据库,形成“雪崩效应”。
解决缓存雪崩
- 设置缓存过期时间的随机性
为了防止缓存中大量数据在同一时间过期,可以为不同的缓存数据设置不同的过期时间,或者在缓存过期时,随机设置过期时间,避免缓存同时失效。
做法:
例如,设置缓存的过期时间时,可以在原有过期时间上加上一些随机的偏移量(例如:TTL = 固定时间 + 随机数),使得缓存失效时间分布在一个较长的时间区间内,从而避免同一时间大量缓存失效。 - 使用缓存预热
对于一些关键的数据,可以在应用启动时或者在特定时刻主动加载到缓存中,以避免缓存未命中直接访问数据库。
做法:
系统可以提前预加载一些常用的缓存数据,在缓存数据过期之前再去刷新,这样就避免了大量请求突然落到数据库上。
缓存击穿
缓存击穿是指当缓存中某个数据在高并发的情况下失效或未命中(一个热点key失效),导致大量请求直接访问数据库,从而造成数据库的瞬间压力增加,甚至可能导致数据库崩溃的情况。
- 缓存击穿的场景
通常,缓存击穿发生在以下几种情况:
热点数据过期:当缓存中存储的是热点数据(即访问量非常大的数据),而这个数据的缓存失效时,多个请求会同时查询数据库,导致数据库被瞬时高并发的请求压垮。
某个键的缓存失效:如果缓存中某个数据的过期时间被设置不合理,或者突然失效,而在同一时刻有大量请求都查询该数据,这些请求将绕过缓存直接访问数据库,产生大量的数据库访问请求。
如何解决缓存击穿
- 缓存数据永不过期
对于一些热点数据,考虑设置缓存永不过期(例如使用"懒加载"机制)。通过定期检查数据的有效性,避免频繁的缓存过期。例如,热点数据定期更新或通过定时任务进行刷新。
实现方式:
对于一些热点数据,可以设置较长的缓存过期时间,或者使用定时任务周期性地更新缓存,而不是依赖缓存自动过期。
2)加锁排队 ,可以使用 双重检查锁(DCL, Double-Checked Locking)和同步锁来确保在缓存击穿的情况下只会有一个请求去查询数据库并更新缓存,而其他请求会等待这个请求完成后再从缓存中获取数据。
双重检查锁的原理
第一次检查:在进入加锁之前先检查缓存是否存在数据,如果存在,直接返回。
加锁:如果缓存中不存在数据,才进入同步代码块。
第二次检查:在加锁之后再次检查缓存,确保在加锁的过程中数据已经被其他线程加载到缓存。
3)互斥锁:使用互斥锁(锁住请求)
当缓存失效时,通过设置互斥锁(如分布式锁)来防止多个请求同时去查询数据库并更新缓存。只有一个请求能够查询数据库并重新加载缓存,其他请求会等待缓存更新完成后,从缓存中获取数据。
实现方式:
使用Redis分布式锁来保证同一时刻只有一个请求会去查询数据库并更新缓存。其他请求会在锁释放后读取缓存。
缓存穿透
缓存穿透是指请求的数据在缓存中根本不存在,且无法通过缓存查询到正确的数据。也就是说,这些请求直接绕过了缓存,访问了后端数据库或其他存储系统。
• 不存在的数据:查询的数据本来就不存在,或者已经被删除,导致缓存中没有该数据。
• 恶意攻击:攻击者故意发起大量不存在的查询请求,绕过缓存直接对数据库进行攻击,可能是为了导致数据库过载,实施DoS攻击(拒绝服务攻击)
Redis的哨兵机制
在 Redis 的主从架构中,由于主从模式是读写分离的,如果主节点(master)挂了,那么将没有主节点来服务客户端的写操作请求,也没有主节点给从节点(slave)进行数据同步了。
Redis 的哨兵机制作用是实现主从节点故障转移。它会监测主节点是否存活,如果发现主节点挂了,它就会选举一个从节点切换为主节点,并且把新主节点的相关信息通知给从节点和客户端。
哨兵其实是一个运行在特殊模式下的 Redis 进程,所以它也是一个节点。从"哨兵"这个名字也可以看得出来,它相当于是“观察者节点”,观察的对象是主从节点。
Redis的集群模式
Redis 集群(Redis Cluster)是一种分布式架构模式,可以将数据分片存储在多个 Redis 实例中,并提供高可用性。它支持无中心架构,节点之间通过 gossip 协议进行通信,采用 slot 机制进行数据分布。
-
Redis 集群模式分类
Redis 支持三种主要的集群模式:
主从复制模式(Master-Slave)
通过 一主多从 方式进行数据冗余,实现读写分离。
适用于高读并发场景。
Sentinel(哨兵)模式
监控主节点状态,自动进行主从切换(故障转移)。
适用于 高可用 场景。
Cluster(分片集群)模式
采用 哈希槽(slot) 机制进行数据分片存储。
每个节点存储一部分数据,支持自动故障转移和高可用性。 -
Redis Cluster 工作原理
Redis Cluster 采用 数据分片(Sharding) 机制:
共有 16384 个槽(slot)。
每个节点负责一部分 slot(如 3 个节点,每个负责约 5461 个 slot)。
一致性 Hash 计算 key -> slot,然后路由到正确的节点。
数据访问流程
客户端连接 Redis 集群中的某个节点。
该节点检查 key 的 slot 归属:
如果 key 属于本节点,直接返回数据。
如果 key 不属于本节点,返回 MOVED 指令,引导客户端访问正确的节点。 -
Redis Cluster 适用场景
高并发读写场景(如:电商秒杀、社交平台)
数据量大且分片存储需求高(如:排行榜、日志存储)
高可用性要求高(如:实时数据缓存)
4.Redis Cluster 相关工具
redis-cli
redis-cli -c -h-p 连接 Redis Cluster
CLUSTER NODES 查看集群节点
CLUSTER INFO 获取集群信息
Gossip 协议
集群节点间的通信协议,定期交换数据。
Redis的大key
大 Key(Big Key) 指的是 单个 key 关联的 value 过大,无论是数据量还是数据结构的复杂度超出了合理范围,都会带来 性能、内存、删除、迁移 等方面的问题。 -
大 Key 的常见类型
(1)单个 key 关联的 value 过大
一个 key 对应的数据量过多,如:
一个字符串 key 存储超大 JSON 或 XML
一个哈希(hash)存储上百万个字段
一个列表(list)有上百万条数据
一个有序集合(sorted set)存储超大排行榜数据
(2)对象嵌套导致的存储过大
hash、list、set、zset 结构中嵌套过多数据。
例如,一个 hash 存储一个用户的所有历史订单数据(上百万条) -
大 Key 带来的问题
(1)查询慢
由于 value 过大,每次访问需要读取大量数据,导致:
查询延迟增加
阻塞 Redis 主线程(Redis 是单线程的,所有操作都是同步执行)
影响其他请求的响应时间
(2)删除和过期慢
Redis 的 DEL 操作是 同步执行 的,删除大 Key 可能会 阻塞 Redis 进程,导致 Redis 处理能力下降。
过期时如果数据量过大,会导致过期清理的 CPU 和 I/O 负担过重,影响整体性能。
(3)迁移/备份/同步影响
在 Redis Cluster(集群)模式 下,节点间迁移数据时:
大 Key 迁移时占用过多带宽,导致其他请求延迟。
如果主从同步过程中传输大 Key,会导致网络拥堵、同步延迟,甚至阻塞复制链路。
(4)内存碎片和内存管理问题
大 Key 存在时,Redis 可能需要分配大块内存,后续删除后容易产生 内存碎片。
AOF 持久化 时,写入大 Key 会导致 AOF 文件膨胀,重写耗时过长。
如何发现大 Key:
方法 1:使用 redis-cli 直接查找
redis-cli --bigkeys
该命令会扫描数据库,统计不同类型的大 Key。
方法 2:使用 scan 结合 MEMORY USAGE
redis-cli --scan | while read key; do
echo "$(redis-cli MEMORY USAGE $key) - $key"
done | sort -nr | head -10
该方法可以 按大小排序 找出最大的 Key。
方法 3:使用 info memory 监控
redis-cli info memory
观察 used_memory_dataset,发现是否存在异常的内存占用 -
解决方案
(1)优化数据结构
字符串(String)大 Key → 拆分成多个 Key 存储(分片存储)
列表(List)大 Key → 限制长度,如 LRANGE key 0 100
哈希(Hash)大 Key → 拆成多个小哈希存储
集合(Set)/有序集合(ZSet) → 采用 分页查询,不要一次取出全部数据
(2)删除优化
避免 DEL key 直接删除大 Key
改用 UNLINK key(非阻塞删除)
UNLINK myBigKey
使用 EXPIRE key 秒数 让其自动过期
Lua 脚本分批删除
lua
local keys = redis.call('LRANGE', KEYS[1], 0, 99)
for _, key in ipairs(keys) do
redis.call('LREM', KEYS[1], 0, key)
end
(3)避免一次性取出大 Key
分页查询
LRANGE biglist 0 100
基于 SCAN 逐步遍历
SCAN cursor MATCH mybigset:* COUNT 100
(4)合理分片
数据分片存储,避免单个 Key 存储超大数据:
user:1000:order:1
user:1000:order:2
(5)监控大 Key
定期执行 bigkeys 命令
结合 Redis Slowlog 监控慢查询
SLOWLOG GET 10
Redis的热key
热 Key(Hot Key) 指的是 访问频率极高的某个 Redis Key,导致该 Key 访问压力过大,可能出现:
Redis CPU/带宽瓶颈:单个 Key 访问量过高,导致 Redis 负载过重。
数据热点:由于访问量集中到某个 Key,影响其他 Key 的访问。
集群负载不均衡:如果是 Redis Cluster 模式,热 Key 可能导致某个节点负载过高,而其他节点资源闲置。
Redis 宕机风险:当大量请求同时访问某个 Key,可能造成 Redis 拒绝服务(Redis 本身是单线程处理请求)。 -
热 Key 的常见场景
热点数据缓存:
比如 “今日热点新闻”、“热门商品”、“微博热搜” 等。
限时活动:
电商大促销、秒杀活动,导致某些商品的库存 Key 被高频访问。
排行榜数据:
例如 “直播间在线人数”、“某用户点赞数”、“某视频播放量” 过于集中。 -
如何检测热 Key?
方法 1:使用 monitor 观察请求
redis-cli monitor
直接监控 Redis 实时执行的命令,观察哪些 Key 被频繁访问。
方法 2:使用 hotkeys 命令(适用于 Redis 7.0+)
redis-cli –hotkeys
Redis 7.0 以上版本提供 hotkeys 命令,可直接统计访问频率最高的 Key。
方法 3:使用 slowlog 发现热点
redis-cli slowlog get 10
查询最近 10 条慢查询日志,查看是否某个 Key 被频繁访问。
方法 4:分析 info keyspace 统计信息
redis-cli info keyspace
观察 expires、keys 数量是否有异常增长。 -
解决热 Key 问题的方法
✅ 方案 1:使用本地缓存(JVM/本地缓存+Redis 双层缓存)
原理:将 Redis 的热点 Key 数据存储到 本地缓存(如 Caffeine、Guava、Ehcache),减少 Redis 访问压力。
示例(Caffeine 本地缓存 + Redis)
浙公网安备 33010602011771号