Skip to content

Redis 入门指南

Redis 数据结构

5 种基本数据类型

┌─────────────────────────────────────────────────────────────────┐
│                      Redis 数据类型                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   String      - 字符串       SDS (Simple Dynamic String)        │
│   Hash        - 哈希表        Dict (Hash Table)                 │
│   List        - 列表         QuickList (压缩列表+双向链表)       │
│   Set         - 集合         IntSet + Hash Table               │
│   Sorted Set  - 有序集合      ZipList + SkipList               │
│                                                                  │
│   高级类型:                                                    │
│   Bitmap      - 位图          String 类型                                                │
│   HyperLogLog - 基数统计      String 类型                                                │
│   Geo         - 地理坐标      Sorted Set                                               │
│   Stream      - 流数据        Radix Tree                                               │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

常用命令

bash
# String
SET key value
GET key
MSET key1 v1 key2 v2
SETNX key value        # 不存在则设置
INCR key               # 原子递增
DECR key               # 原子递减
EXPIRE key 10          # 10秒过期
SETEX key 10 value     # 设置值并指定过期时间

# Hash
HSET user:1 name "张三"
HGET user:1 name
HGETALL user:1
HDEL user:1 name
HINCRBY user:1 age 1

# List
LPUSH list a b c      # 左边入栈
RPUSH list d e        # 右边入栈
LPOP list             # 左边出栈
RPOP list             # 右边出栈
LRANGE list 0 -1      # 获取所有元素
LLEN list             # 列表长度

# Set
SADD tags java python
SMEMBERS tags         # 获取所有成员
SISMEMBER tags java   # 是否存在
SINTER tag1 tag2      # 交集
SUNION tag1 tag2      # 并集

# Sorted Set
ZADD leaderboard 100 "张三"
ZADD leaderboard 200 "李四"
ZRANGE leaderboard 0 -1     # 按分数升序
ZREVRANGE leaderboard 0 9   # 按分数降序前10
ZSCORE leaderboard "张三"    # 获取分数

💡 实战场景

  • String:缓存用户信息、Token、计数器
  • Hash:存储对象(如用户资料)
  • List:消息队列、排行榜
  • Set:标签系统、好友关系
  • ZSet:有序排行榜、延迟队列

Redis 持久化

RDB(Redis Database)

┌─────────────────────────────────────────────────────────────────┐
│                         RDB 持久化                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   原理:定时生成数据快照                                         │
│                                                                  │
│   触发方式:                                                    │
│   1. 定时任务(redis.conf 配置)                               │
│   2. BGSAVE 命令                                               │
│   3. SHUTDOWN 时                                               │
│   4. FLUSHDB + AOF 关闭时                                      │
│                                                                  │
│   优点:恢复速度快,适合备份                                    │
│   缺点:可能丢失最后一次快照后的数据                            │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

AOF(Append Only File)

┌─────────────────────────────────────────────────────────────────┐
│                         AOF 持久化                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   原理:记录每个写命令                                           │
│                                                                  │
│   同步策略:                                                    │
│   - always     每条命令同步                                     │
│   - everysec   每秒同步(推荐)                                │
│   - no         由系统同步                                      │
│                                                                  │
│   重写机制:                                                    │
│   BGREWRITEAOF - 压缩 AOF 文件                                 │
│                                                                  │
│   优点:数据安全性高                                            │
│   缺点:文件体积大,恢复速度慢                                  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

面试点:⭐⭐⭐⭐ 实战重要性:⭐⭐⭐⭐


Redis 过期策略

三种过期策略

bash
# 1. 定时删除(定时扫描)
# 设置过期时间时创建定时器,到期删除
# 优点:内存友好
# 缺点:CPU 压力大

# 2. 惰性删除(访问时检查)
# 访问 key 时检查是否过期
# 优点:CPU 友好
# 缺点:内存可能泄漏

# 3. 定期删除(折中方案)
# 周期性扫描,定期检查
# Redis 默认使用惰性删除 + 定期删除

内存淘汰策略

bash
# 当内存达到 maxmemory 时
maxmemory-policy volatile-lru

# 策略选项:
# - noeviction    不淘汰,返回错误
# - allkeys-lru   所有 key LRU 淘汰
# - volatile-lru  有过期时间的 key LRU 淘汰
# - allkeys-random 所有 key 随机淘汰
# - volatile-random 有过期时间的 key 随机淘汰
# - volatile-ttl   有过期时间的 key 优先淘汰 TTL 短的

Redis 集群

主从复制

┌─────────────────────────────────────────────────────────────────┐
│                      主从复制架构                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│                      ┌──────────┐                               │
│                      │  Master   │                               │
│                      └────┬─────┘                               │
│                           │                                     │
│            ┌──────────────┼──────────────┐                     │
│            │              │              │                     │
│            ▼              ▼              ▼                     │
│       ┌────────┐    ┌────────┐    ┌────────┐                  │
│       │Slave 1 │    │Slave 2 │    │Slave 3 │                  │
│       └────────┘    └────────┘    └────────┘                  │
│                                                                  │
│   主节点:写 + 读                                                │
│   从节点:读(分担压力)                                         │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

哨兵模式

bash
# 哨兵监控主从节点
# 自动故障转移
# 客户端连接哨兵获取主节点地址

# 配置文件:sentinel.conf
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000

Cluster 模式

┌─────────────────────────────────────────────────────────────────┐
│                      Redis Cluster 架构                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   16384 个槽位分片                                              │
│                                                                  │
│   ┌────────┐  ┌────────┐  ┌────────┐                          │
│   │ Node 1 │  │ Node 2 │  │ Node 3 │                          │
│   │ 0-5460 │  │5461-10922│10923-16383│                      │
│   └────┬───┘  └────┬───┘  └────┬───┘                          │
│        │           │           │                               │
│        ▼           ▼           ▼                               │
│   ┌────────┐  ┌────────┐  ┌────────┐                          │
│   │Master 1│  │Master 2│  │Master 3│                          │
│   └────────┘  └────────┘  └────────┘                          │
│        │           │           │                               │
│        ▼           ▼           ▼                               │
│   ┌────────┐  ┌────────┐  ┌────────┐                          │
│   │ Slave 1 │  │ Slave 2 │  │ Slave 3 │                          │
│   └────────┘  └────────┘  └────────┘                          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

面试点:⭐⭐⭐⭐


分布式锁

Redis 分布式锁

java
// 加锁
public boolean lock(String key, String value, long expireTime) {
    String result = redisTemplate.opsForValue()
        .setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
    return Boolean.TRUE.equals(result);
}

// 解锁(Lua 脚本保证原子性)
public boolean unlock(String key, String value) {
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                    "then return redis.call('del', KEYS[1]) " +
                    "else return 0 end";

    return redisTemplate.execute(
        new DefaultRedisScript<>(script, Long.class),
        Collections.singletonList(key),
        value
    ) == 1L;
}

Redisson 实现

java
// Redisson 分布式锁
RLock lock = redissonClient.getLock("order:lock:" + orderId);
try {
    // 等待锁30秒,锁定后自动60秒释放
    if (lock.tryLock(30, 60, TimeUnit.SECONDS)) {
        // 业务逻辑
    }
} finally {
    lock.unlock();
}

面试点:⭐⭐⭐⭐⭐ 实战重要性:⭐⭐⭐⭐⭐


缓存问题

缓存穿透

💡 实战场景:黑客恶意请求不存在的数据,每次都打到数据库

┌─────────────────────────────────────────────────────────────────┐
│                      缓存穿透示意                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   请求 ──▶ Redis ──▶ DB ──▶ 返回 NULL                         │
│              ↑         ↑                                        │
│              │         │                                        │
│         缓存不存在  数据库也不存在                                │
│              │         │                                        │
│              ←─────────┘                                        │
│            每次请求都打满数据库                                  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

解决方案

java
// 解决方案1:布隆过滤器
BloomFilter<String> bloomFilter = BloomFilter.create(
    Funnels.stringFunnel(Charset.defaultCharset()),
    1000000,  // 预期元素数量
    0.01      // 误判率
);

// 查询前先检查布隆过滤器
public User getUser(Long id) {
    String key = "user:" + id;

    // 布隆过滤器判断
    if (!bloomFilter.mightContain(key)) {
        return null;  // 一定不存在
    }

    // 缓存查询
    User user = redisTemplate.opsForValue().get(key);
    if (user != null) {
        return user;
    }

    // 数据库查询
    user = userDao.findById(id);
    if (user != null) {
        redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);
        bloomFilter.put(key);
    }

    return user;
}
java
// 解决方案2:缓存空值
if (user == null) {
    redisTemplate.opsForValue().set(key, "NULL", 5, TimeUnit.MINUTES);
} else {
    redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);
}

缓存击穿

💡 实战场景:热点 key 过期,瞬间大量请求同时打满数据库

┌─────────────────────────────────────────────────────────────────┐
│                      缓存击穿示意                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   热点 key 过期                                                 │
│        │                                                        │
│        ▼                                                        │
│   ┌─────────────────────────────────────────────────┐          │
│   │        瞬间大量请求同时发现缓存不存在            │          │
│   └─────────────────────────────────────────────────┘          │
│        │                                                        │
│        ▼                                                        │
│       DB                                                        │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

解决方案

java
// 解决方案:互斥锁
public User getUserMutex(Long id) {
    String key = "user:" + id;
    User user = redisTemplate.opsForValue().get(key);

    if (user == null) {
        String lockKey = "lock:" + key;
        String lockValue = UUID.randomUUID().toString();

        if (redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 10, TimeUnit.SECONDS)) {
            try {
                user = userDao.findById(id);
                redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);
            } finally {
                redisTemplate.delete(lockKey);
            }
        } else {
            // 其他线程等待后重试
            try { Thread.sleep(50); } catch (InterruptedException ignored) {}
            return getUserMutex(id);
        }
    }

    return user;
}

缓存雪崩

💡 实战场景:大量缓存同时过期,导致数据库压力过大

┌─────────────────────────────────────────────────────────────────┐
│                      缓存雪崩示意                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   大量 key 同时过期                                              │
│        │                                                        │
│        ▼                                                        │
│   ┌─────────────────────────────────────────────────┐          │
│   │        大量请求同时发现缓存过期                   │          │
│   └─────────────────────────────────────────────────┘          │
│        │                                                        │
│        ▼                                                        │
│       DB                                                        │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

解决方案

java
// 解决方案1:过期时间加随机值
redisTemplate.opsForValue().set(key, value, 1 + random.nextInt(5), TimeUnit.HOURS);

// 解决方案2:热点数据永不过期 + 异步更新

// 解决方案3:Redis Cluster 高可用

面试点:⭐⭐⭐⭐⭐ 实战重要性:⭐⭐⭐⭐⭐


总结

Redis 核心知识点

知识点面试频率实战重要性
5 种数据结构⭐⭐⭐⭐⭐⭐⭐⭐⭐
持久化 RDB/AOF⭐⭐⭐⭐⭐⭐⭐⭐
过期策略⭐⭐⭐⭐⭐⭐⭐
内存淘汰⭐⭐⭐⭐⭐⭐⭐
主从复制⭐⭐⭐⭐⭐⭐⭐
哨兵/Cluster⭐⭐⭐⭐⭐⭐⭐
分布式锁⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
缓存穿透/击穿/雪崩⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

⚠️ 易错点提醒

  1. Redis 单线程是指处理命令单线程,但持久化是后台线程
  2. 分布式锁要使用 Lua 脚本保证原子性
  3. 缓存穿透、击穿、雪崩是不同的概念和解决方案
  4. 布隆过滤器有误判率,但不会漏判
  5. String 是最常用的类型,但 Hash 存储对象更节省内存