目录

Redis面试篇

Redis面试篇


Redis面试篇

1.什么是Redis?作用是什么?

Redis(Remote Dictionary Server)是一个 基于内存的高性能 NoSQL 数据库 ,通常用于 缓存分布式存储消息队列 。它支持 键值(key-value)存储 ,并且可以存储 字符串、哈希、列表、集合、有序集合等多种数据结构

Redis 的主要作用

  1. 缓存(提高系统性能)

    • 存储热点数据 ,减少数据库访问压力,提高查询速度。
    • 例如:存储用户信息、文章内容、商品信息等,避免每次都查数据库。
  2. 分布式锁(保证并发安全)

    • 在高并发场景下,多个线程/进程可能同时访问一个资源,Redis 的 SETNXRedisson 可以实现 分布式锁 ,防止数据冲突。
    • 例如:限流、秒杀活动、库存扣减。
  3. 计数器和限流(防止系统被滥用)

    • 适用于访问量统计、限流(如接口请求次数限制)。
    • 例如:某接口 1 分钟内最多访问 10 次 ,超过就返回错误。
  4. Session 共享(解决分布式会话问题)

    • 在分布式系统中,多个服务器需要共享 Session(如用户登录状态),可以使用 Redis 统一存储。
  5. 消息队列(异步处理任务)

    • Redis 的 ListPub/Sub 可用于实现 消息队列 ,提高系统吞吐量。
    • 例如: 订单系统消息队列异步处理支付、物流
  6. 排行榜(游戏、社交场景)

    • 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 是 基于内存 的数据库,但为了防止数据丢失,它提供了 两种持久化机制

  1. RDB(Redis Database)—— 快照存储
  2. AOF(Append Only File)—— 追加日志存储

1. RDB(快照存储)

原理 :Redis 以 二进制快照 的形式将数据库的 整个数据 保存到磁盘文件( .rdb )中,在发生故障时可以恢复。

特点

✅ 适用于 大规模数据的定期备份 ,恢复速度快。 ✅ 占用空间小,存储效率高。 ❌ 如果 Redis 意外崩溃 ,则 最后一次快照之后的数据可能丢失

2. AOF(追加日志)

原理 :Redis 记录所有的 写操作命令 (如 SETDEL ),并将它们 以日志形式.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" 表示加锁成功。

🔴 问题

  1. 锁可能无法释放 :如果服务崩溃,锁不会自动释放,导致死锁。
  2. 误删其他线程的锁 :如果锁过期后被其他线程重新加锁,当前线程仍然认为自己持有锁,会误删别人的锁。

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 分布式锁核心要点

  1. 使用 SETNX + PX 设定锁的过期时间 ,避免死锁。
  2. 删除锁时使用 Lua 脚本 ,保证 原子性
  3. 使用 Redisson 进行优化 ,提供自动续期,避免误删锁。
  4. 适用于高并发场景 (如秒杀、库存扣减),但可靠性不如 Zookeeper。

Redis 分布式锁虽然 高效 ,但并非绝对可靠,建议使用 RedissonZookeeper 进行优化! 🚀