Redis面试篇
Redis面试篇
Redis面试篇
1.什么是Redis?作用是什么?
Redis(Remote Dictionary Server)是一个 基于内存的高性能 NoSQL 数据库 ,通常用于 缓存 、 分布式存储 和 消息队列 。它支持 键值(key-value)存储 ,并且可以存储 字符串、哈希、列表、集合、有序集合等多种数据结构
Redis 的主要作用
缓存(提高系统性能)
- 存储热点数据 ,减少数据库访问压力,提高查询速度。
- 例如:存储用户信息、文章内容、商品信息等,避免每次都查数据库。
分布式锁(保证并发安全)
- 在高并发场景下,多个线程/进程可能同时访问一个资源,Redis 的 SETNX 、 Redisson 可以实现 分布式锁 ,防止数据冲突。
- 例如:限流、秒杀活动、库存扣减。
计数器和限流(防止系统被滥用)
- 适用于访问量统计、限流(如接口请求次数限制)。
- 例如:某接口 1 分钟内最多访问 10 次 ,超过就返回错误。
Session 共享(解决分布式会话问题)
- 在分布式系统中,多个服务器需要共享 Session(如用户登录状态),可以使用 Redis 统一存储。
消息队列(异步处理任务)
- Redis 的
List
和Pub/Sub
可用于实现 消息队列 ,提高系统吞吐量。 - 例如: 订单系统 → 消息队列 → 异步处理支付、物流 。
- Redis 的
排行榜(游戏、社交场景)
- Redis 的 有序集合(Sorted Set) 可以存储分数排名,如游戏积分榜、热搜榜。
2.什么是缓存穿透、缓存击穿、缓存雪崩
2.1缓存穿透
缓存穿透是指客户端频繁访问一些不存在的缓存数据,由于缓存中没有这些数据的记录,每次请求都直接访问数据库,导致数据库压力增大,但是数据库中也不存在(就是指大量请求不存在的资源,大量请求不存在的资源大概率就是被黑客攻击了……)
如何解决? 缓存空值:当查询一个不存在的key时,先访问缓存,缓存中没有访问数据库,数据库中也没有用的话就用redis做一个空标记 ,将空结果也写入缓存,并设置一个较短的过期时间 。下次再遇到相同的请求时判断是否存在空标记 key,存在这个空标记则直接返回缓存中的空值,避免再次查询数据库。(但这种标记过多的话会消耗资源,不推荐)
使用分布式锁:当请求发现缓存不存在时,可以使用分布式锁机制,避免多个相同的请求同时访问数据库。这样,只让一个请求去加载数据,其他请求等待,从而减轻数据库压力.
布隆过滤器 :布隆过滤器是一种数据结构,在查询缓存和数据库之前,利用布隆过滤器来存储这个 key ,判断key是否存在,存在则直接放行.如果该key不存在则直接被拦截返回,不必查询缓存或数据库,节约存储空间。
对疯狂请求不存在数据的ip进行拉黑
缓存穿透无敌解决方法(能用上的方法全用上 ^_^ ) 布隆过滤器+空对象+分布式锁:
1.当缓存不存在时,先通过布隆过滤器进行初步筛选,
2.如果布隆过滤器判定数据可能存在,则检查是否存在空对象缓存。
3.如果空对象缓存不存在,再使用分布式锁防止多个相同请求同时访问数据库。
4.最后,如果数据库查询结果为空,将结果缓存为空对象,以防后续重复查询
2.2缓存击穿
缓存击穿 是指某个 热点数据 在缓存中失效的 瞬间 有大量的并发请求同时访问该数据,由于该数据在缓存中失效,大量请求同时访问数据库,造成数据库压力骤增。这种情况通常发生在热点数据或访问频繁的数据上。
如何解决? 互斥锁机制 :当缓存失效时,通过加锁的方式保证只有一个线程去查询数据库并更新缓存,其他线程等待缓存更新完成后再获取数据。
缓存不设置过期时间:对于极为重要的热点数据,可以设置其缓存永不过期,同时后台启动线程定期刷新该缓存,但如果重要的热点数据过多,不推荐此方法
监控热门数据,实时调整key的过期时长
2.3缓存雪崩
redis中 大量key同时过期 ,导致大量请求直接访问数据库,瞬间引发数据库压力激增,甚至导致数据库崩溃。
如何解决? 缓存的过期时间使用随机值,这样减少了大量缓存同时过期的情况.
实时调整,监控哪些数据是热门数据,实时的调整key的过期时长.
使用锁机制,未获取锁的请求只能进行等待
3.redis如何持久化
Redis 是 基于内存 的数据库,但为了防止数据丢失,它提供了 两种持久化机制 :
- RDB(Redis Database)—— 快照存储
- AOF(Append Only File)—— 追加日志存储
1. RDB(快照存储)
原理
:Redis 以
二进制快照
的形式将数据库的
整个数据
保存到磁盘文件(
.rdb
)中,在发生故障时可以恢复。
特点 :
✅ 适用于 大规模数据的定期备份 ,恢复速度快。 ✅ 占用空间小,存储效率高。 ❌ 如果 Redis 意外崩溃 ,则 最后一次快照之后的数据可能丢失 。
2. AOF(追加日志)
原理
:Redis 记录所有的
写操作命令
(如
SET
、
DEL
),并将它们
以日志形式
(
.aof
文件)追加到磁盘,Redis
重启时可重放这些命令恢复数据
。
特点 :
✅ 数据安全 ,即使 Redis 意外崩溃 , AOF 也能最大限度减少数据丢失 。 ✅ 适用于 高实时性数据 (例如:秒杀、交易系统)。 ❌ 日志文件较大 ,可能会影响 Redis 启动速度。
4. Redis 的过期策略和内存淘汰策略
Redis 作为 内存数据库 ,存储空间是有限的,因此需要 过期策略 和 内存淘汰策略 来管理数据。
4.1 Redis 过期策略(如何删除过期数据)
Redis 允许 设置键的过期时间 (TTL),当键过期后,就会被删除。Redis 提供了 三种删除策略 :
(1)定期删除(默认)
机制 :Redis 每 100ms 进行一次 过期键的扫描 ,随机检查一部分过期键,并删除过期的键。
✅ 优点 :减少了系统负担,不会影响 Redis 性能。 ❌ 缺点 :如果过期键过多,可能 不会一次性删除完 ,导致 内存占用增加 。
(2)惰性删除
机制 :当 客户端访问一个键 时,Redis 发现它已经 过期 ,就会 立刻删除 这个键。
✅ 优点 :仅在访问时删除,减少 CPU 负担。 ❌ 缺点 :如果某些键 一直不被访问 ,它们可能 不会被删除 ,从而 占用内存 。
(3)定期删除 + 惰性删除(默认策略)
Redis 默认使用 定期删除 + 惰性删除 ,既能避免频繁扫描,也能确保访问时自动删除过期键。
4.2Redis内存淘汰策略
如果 Redis 占满了可用内存 ,就需要删除一些数据来释放空间。Redis 提供了 6 种内存淘汰策略 :
策略名称 | 说明 | 适用场景 |
---|---|---|
noeviction(默认) | 不删除任何数据,直接报错(写入失败) | 适合 持久化数据 ,不希望丢失 |
allkeys-lru | 删除 最久未使用 的键(全局 LRU) | 适用于 缓存 ,需要高命中率 |
volatile-lru | 删除 设置了过期时间 的最久未使用键 | 适用于 缓存+持久化 混合场景 |
allkeys-random | 随机删除 任何键 | 适用于 无固定热点 的场景 |
volatile-random | 只随机删除 有过期时间 的键 | 适用于 部分数据需要持久化 |
volatile-ttl | 优先删除 过期时间最短 的键 | 适用于 定期更新数据 的场景 |
5.Redis的分布式锁
Redis 的分布式锁是一种 基于 Redis 实现的分布式并发控制机制 ,用于保证多个进程或线程在分布式环境下对共享资源的 互斥访问 。
5.1 为什么需要分布式锁?
在 分布式系统 中,多个应用可能会 同时 操作同一个资源(如库存、订单等),导致数据 不一致 ,这时就需要 分布式锁 来保证 同一时间 只有一个进程能执行操作。
示例问题
- 秒杀系统 :多个用户同时抢购,导致库存超卖。
- 订单支付 :用户 A 和 B 同时支付同一笔订单,可能出现 重复扣款 。
- 任务调度 :多个服务器执行同一任务,可能导致任务被执行 多次 。
5.2 Redis 分布式锁的基本实现
使用
SETNX
(SET if Not Exists) 命令创建锁:
SETNX mylock "locked"
锁的基本实现
public boolean tryLock(String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
return "OK".equals(result);
}
✅ 解释 :
lockKey
:锁的 key(比如"order_lock"
)。requestId
:每个锁 唯一 的 ID,保证同一客户端能释放自己的锁。"NX"
:只在 key 不存在 时才设置(保证互斥性)。"PX expireTime"
:自动 过期时间 (避免死锁)。"OK".equals(result)
:返回"OK"
表示加锁成功。
🔴 问题 :
- 锁可能无法释放 :如果服务崩溃,锁不会自动释放,导致死锁。
- 误删其他线程的锁 :如果锁过期后被其他线程重新加锁,当前线程仍然认为自己持有锁,会误删别人的锁。
5.3分布式锁的优化
(1)设置锁的自动过期时间
防止 死锁 :
SET order_lock "locked" NX PX 10000
NX表示key不存在时才创建,保证互斥性
PX 10000 → 10 秒后自动释放锁
(2)使用 Redisson 实现分布式锁
是 Redis 官方推荐的 Java 客户端,提供了 高效 的分布式锁实现:
RLock lock = redissonClient.getLock("order_lock");
try {
lock.lock(10, TimeUnit.SECONDS);
// 执行业务逻辑
} finally {
lock.unlock();//释放锁
}
Redisson 优势 :
自动续期(Watchdog 机制):如果持有锁的线程还在执行,Redisson 会自动延长锁的过期时间。 避免误删锁:保证释放的是自己的锁。
Redis 分布式锁核心要点 :
- 使用
SETNX
+PX
设定锁的过期时间 ,避免死锁。 - 删除锁时使用 Lua 脚本 ,保证 原子性 。
- 使用 Redisson 进行优化 ,提供自动续期,避免误删锁。
- 适用于高并发场景 (如秒杀、库存扣减),但可靠性不如 Zookeeper。
Redis 分布式锁虽然 高效 ,但并非绝对可靠,建议使用 Redisson 或 Zookeeper 进行优化! 🚀