Skip to content

Redis面试题

1. Redis的基本概念

问题:什么是Redis?

答案

  • Redis:开源的内存数据结构存储系统,可以用作数据库、缓存和消息中间件。
  • 特点
    • 基于内存存储
    • 支持多种数据结构
    • 支持持久化
    • 支持主从复制
    • 支持集群
    • 高性能

2. Redis的数据结构

问题:Redis支持哪些数据结构?

答案

  • String:字符串
  • Hash:哈希表
  • List:列表
  • Set:集合
  • Sorted Set:有序集合
  • Bitmap:位图
  • HyperLogLog:基数统计
  • Geo:地理位置

3. Redis的持久化

问题:Redis有哪些持久化方式?

答案

  • RDB(Redis Database)
    • 快照持久化
    • 生成二进制文件
    • 适合备份
    • 恢复速度快
  • AOF(Append Only File)
    • 日志持久化
    • 记录写操作
    • 数据更安全
    • 恢复速度慢

4. RDB和AOF的区别

问题:RDB和AOF有什么区别?

答案

  • RDB
    • 快照持久化,生成二进制文件。
    • 文件小,恢复速度快。
    • 可能丢失数据。
    • 适合备份。
  • AOF
    • 日志持久化,记录写操作。
    • 文件大,恢复速度慢。
    • 数据更安全。
    • 适合实时性要求高的场景。

5. Redis的主从复制

问题:Redis的主从复制原理是什么?

答案

  1. 从库连接主库:发送SYNC命令。
  2. 主库执行BGSAVE:生成RDB文件。
  3. 主库发送RDB文件:发送给从库。
  4. 从库加载RDB文件:恢复数据。
  5. 主库发送写命令:发送给从库。
  6. 从库执行写命令:保持数据一致。

6. Redis的哨兵

问题:什么是Redis哨兵?

答案

  • 哨兵:Redis的高可用解决方案。
  • 作用
    • 监控主从节点
    • 故障自动转移
    • 配置中心
    • 通知机制

7. Redis的集群

问题:什么是Redis集群?

答案

  • 集群:Redis的分布式解决方案。
  • 特点
    • 数据分片
    • 高可用
    • 自动故障转移
    • 线性扩展

8. Redis的事务

问题:Redis的事务有什么特点?

答案

  • 特点
    • 不支持回滚。
    • 原子性执行。
    • 单独隔离。
  • 命令
    • MULTI:开启事务。
    • EXEC:执行事务。
    • DISCARD:取消事务。
    • WATCH:监视键。

示例

bash
MULTI
SET key1 value1
SET key2 value2
EXEC

9. Redis的管道

问题:什么是Redis管道?

答案

  • 管道:批量执行命令,减少网络开销。
  • 作用
    • 提高性能。
    • 减少网络往返。
    • 批量操作。

示例

java
Jedis jedis = new Jedis("localhost", 6379);
Pipeline pipeline = jedis.pipelined();
for (int i = 0; i < 100; i++) {
    pipeline.set("key" + i, "value" + i);
}
List<Object> results = pipeline.syncAndReturnAll();

10. Redis的缓存

问题:如何使用Redis作为缓存?

答案

  • 缓存策略
    • Cache Aside:旁路缓存
    • Read Through:读穿透
    • Write Through:写穿透
    • Write Behind:异步写入
  • 缓存更新
    • 先更新数据库,再删除缓存。
    • 先删除缓存,再更新数据库。
    • 延迟双删。

11. Redis的缓存穿透

问题:什么是缓存穿透?如何解决?

答案

  • 缓存穿透:查询不存在的数据,绕过缓存直接查询数据库。
  • 解决方法
    • 缓存空值。
    • 使用布隆过滤器。
    • 请求参数校验。

12. Redis的缓存击穿

问题:什么是缓存击穿?如何解决?

答案

  • 缓存击穿:热点数据过期,大量请求直接查询数据库。
  • 解决方法
    • 设置热点数据永不过期。
    • 使用互斥锁。
    • 提前刷新缓存。

13. Redis的缓存雪崩

问题:什么是缓存雪崩?如何解决?

答案

  • 缓存雪崩:大量缓存同时失效,大量请求直接查询数据库。
  • 解决方法
    • 设置不同的过期时间。
    • 使用缓存预热。
    • 使用互斥锁。
    • 限流降级。

14. Redis的分布式锁

问题:如何使用Redis实现分布式锁?

答案

java
public class RedisLock {
    private Jedis jedis;
    private String lockKey;
    private String lockValue;
    private int expireTime;
    
    public boolean tryLock() {
        String result = jedis.set(lockKey, lockValue, "NX", "EX", expireTime);
        return "OK".equals(result);
    }
    
    public boolean unlock() {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long result = (Long) jedis.eval(script, Collections.singletonList(lockKey), lockValue);
        return result == 1;
    }
}

15. Redis的Lua脚本

问题:如何使用Redis的Lua脚本?

答案

java
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Long result = (Long) jedis.eval(script, Collections.singletonList(lockKey), lockValue);

16. Redis的发布订阅

问题:如何使用Redis的发布订阅?

答案

java
// 发布者
jedis.publish("channel", "message");

// 订阅者
jedis.subscribe(new JedisPubSub() {
    @Override
    public void onMessage(String channel, String message) {
        System.out.println("Received: " + message);
    }
}, "channel");

17. Redis的限流

问题:如何使用Redis实现限流?

答案

java
public boolean allowRequest(String key, int limit, int period) {
    String script = "local current = redis.call('incr', KEYS[1]) if tonumber(current) == 1 then redis.call('expire', KEYS[1], ARGV[1]) end if tonumber(current) > tonumber(ARGV[2]) then return 0 else return 1 end";
    Long result = (Long) jedis.eval(script, Collections.singletonList(key), String.valueOf(period), String.valueOf(limit));
    return result == 1;
}

18. Redis的计数器

问题:如何使用Redis实现计数器?

答案

java
// 计数
jedis.incr("counter");

// 指定增量
jedis.incrBy("counter", 10);

// 计数并设置过期时间
jedis.incr("counter");
jedis.expire("counter", 60);

19. Redis的排行榜

问题:如何使用Redis实现排行榜?

答案

java
// 添加分数
jedis.zadd("ranking", 100, "user1");
jedis.zadd("ranking", 200, "user2");

// 获取排名
Long rank = jedis.zrank("ranking", "user1");

// 获取分数
Double score = jedis.zscore("ranking", "user1");

// 获取排行榜
Set<Tuple> ranking = jedis.zrevrangeWithScores("ranking", 0, 9);

20. Redis的地理位置

问题:如何使用Redis的地理位置?

答案

java
// 添加位置
jedis.geoadd("locations", 116.404269, 39.915705, "Beijing");
jedis.geoadd("locations", 121.473701, 31.230416, "Shanghai");

// 计算距离
Double distance = jedis.geodist("locations", "Beijing", "Shanghai", "km");

// 查询附近的位置
List<GeoRadiusResponse> nearby = jedis.georadius("locations", 116.404269, 39.915705, 1000, GeoUnit.KM);

21. Redis的位图

问题:如何使用Redis的位图?

答案

java
// 设置位
jedis.setbit("bitmap", 0, true);
jedis.setbit("bitmap", 1, false);

// 获取位
boolean bit = jedis.getbit("bitmap", 0);

// 统计位
long count = jedis.bitcount("bitmap");

// 位运算
jedis.bitop(BitOP.AND, "result", "bitmap1", "bitmap2");

22. Redis的HyperLogLog

问题:如何使用Redis的HyperLogLog?

答案

java
// 添加元素
jedis.pfadd("hll", "user1", "user2", "user3");

// 统计基数
long count = jedis.pfcount("hll");

// 合并HyperLogLog
jedis.pfmerge("result", "hll1", "hll2");

23. Redis的内存优化

问题:如何优化Redis的内存使用?

答案

  • 使用合适的数据结构
    • 使用Hash代替多个String。
    • 使用List代替多个String。
    • 使用Sorted Set代替List。
  • 使用压缩
    • 开启压缩。
    • 使用压缩列表。
  • 设置过期时间
    • 为数据设置过期时间。
    • 使用LRU淘汰策略。
  • 使用分片
    • 使用集群分片。
    • 使用客户端分片。

24. Redis的性能优化

问题:如何优化Redis的性能?

答案

  • 使用Pipeline:批量执行命令。
  • 使用Lua脚本:减少网络往返。
  • 使用连接池:复用连接。
  • 使用集群:分布式部署。
  • 使用持久化:根据场景选择RDB或AOF。
  • 使用慢查询日志:分析慢查询。
  • 使用监控工具:监控Redis性能。

25. Redis的最佳实践

问题:使用Redis的最佳实践有哪些?

答案

  • 选择合适的数据结构。
  • 设置合理的过期时间。
  • 使用Pipeline批量操作。
  • 使用Lua脚本保证原子性。
  • 使用分布式锁保证并发安全。
  • 使用布隆过滤器防止缓存穿透。
  • 使用互斥锁防止缓存击穿。
  • 使用不同的过期时间防止缓存雪崩。
  • 使用集群提高可用性。
  • 使用哨兵实现高可用。