Redis

Redis:原理剖析、常用功能与项目实战

Redis 是一款高性能的内存数据库,支持丰富的数据结构,常用于缓存、分布式锁、消息队列等场景。本文从原理、使用方式、常见问题与调优等角度系统介绍 Redis 的核心能力与实战经验。


一、Redis 常见数据结构与使用

类型 描述 示例用途
String 字符串类型,基本键值存储 缓存单个对象数据
Hash 类似于 Map 的结构 存储用户信息
List 链表结构,支持队列操作 消息队列、任务列表
Set 无序集合,自动去重 标签系统、抽奖活动
ZSet 有序集合,按分值排序 排行榜、延迟队列
// 示例:存储用户信息
redisTemplate.opsForHash().put("user:1001", "name", "Tom");
redisTemplate.opsForHash().put("user:1001", "age", "20");

二、Redis 高级功能

1. 过期时间与持久化

redisTemplate.opsForValue().set("code:email", "1234", Duration.ofMinutes(5));

2. 分布式锁(简易实现)

Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock:order", "1", Duration.ofSeconds(10));
if (Boolean.TRUE.equals(lock)) {
    try {
        // 执行业务逻辑
    } finally {
        redisTemplate.delete("lock:order");
    }
}

3. 发布订阅(Pub/Sub)

redisTemplate.convertAndSend("chat", "Hello from user1!");

三、项目实战:舆情系统数据加速场景

在舆情监测系统中,为降低 MySQL 压力,我们对以下数据使用 Redis 缓存优化:

  • 热门搜索词缓存(ZSet)
  • 用户操作记录(List)
  • 高频数据快照(String / Hash)
// 热门关键词打分
redisTemplate.opsForZSet().incrementScore("hotwords", "TikTok", 1);

// 获取排行榜
Set<String> topKeywords = redisTemplate.opsForZSet().reverseRange("hotwords", 0, 9);

// 用户行为日志
redisTemplate.opsForList().leftPush("user:log:123", "clicked:news1");
redisTemplate.expire("user:log:123", Duration.ofDays(1));

// 快照缓存
Map<String, String> article = Map.of("title", "事件分析", "content", "详细内容...");
redisTemplate.opsForHash().putAll("article:snapshot:456", article);
redisTemplate.expire("article:snapshot:456", Duration.ofHours(1));

四、常见问题与调优建议

1. 缓存穿透

方案一:缓存空值

String value = redisTemplate.opsForValue().get("user:9999");
if (value == null) {
    value = dbService.query("user:9999");
    redisTemplate.opsForValue().set("user:9999", "", Duration.ofMinutes(5));
}

方案二:布隆过滤器(Guava)

BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), 1000000);
filter.put("user:1001");
if (!filter.mightContain("user:9999")) {
    return null;
}

2. 缓存击穿

方案:互斥锁 + 双重检查

String value = redisTemplate.opsForValue().get("hot:article:1");
if (value == null) {
    Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock:article:1", "1", Duration.ofSeconds(5));
    if (Boolean.TRUE.equals(lock)) {
        try {
            value = dbLoad("article:1");
            redisTemplate.opsForValue().set("hot:article:1", value, Duration.ofMinutes(5));
        } finally {
            redisTemplate.delete("lock:article:1");
        }
    } else {
        Thread.sleep(50);
        return redisTemplate.opsForValue().get("hot:article:1");
    }
}

3. 缓存雪崩

方案:过期时间添加随机因子

int base = 300; // 秒
int rand = new Random().nextInt(300);
redisTemplate.opsForValue().set("key", value, Duration.ofSeconds(base + rand));

4. 内存膨胀 / 淘汰失败

  • 使用 MEMORY STATS, INFO 监控内存
  • 设置淘汰策略:allkeys-lru
  • 拒绝大 Key(避免 List/Set 里百万元素)

5. 禁止阻塞型命令

  • 禁止:KEYS *FLUSHALL
  • 替代:SCAN 进行分页遍历
Cursor<byte[]> cursor = redisTemplate.getConnectionFactory().getConnection()
    .scan(ScanOptions.scanOptions().match("user:*").count(100).build());

五、持久化与主从复制

1. 持久化机制

Redis 提供两种主要的持久化机制:

  • RDB(快照):周期性保存数据快照至磁盘,性能影响小,适合灾难恢复。
  • AOF(追加日志):记录每次写操作,重启时通过日志重放恢复数据,保障更高数据完整性。

混合持久化(RDB + AOF):结合两者优点,在 Redis 7 中已为默认配置。

配置示例(redis.conf):

save 900 1
appendonly yes
appendfilename "appendonly.aof"

2. 主从复制与高可用

  • Redis 支持一主多从架构,从节点只读。
  • 主节点故障后可通过 Sentinel 实现自动主从切换。
  • Redis Cluster 实现数据分片 + 高可用(多主多从)。

部署示意:

主节点(Master)
  ├── 从节点1(Slave)
  └── 从节点2(Slave)

六、Redis 在分布式系统中的应用场景

场景 使用方式 示例代码/说明
分布式锁 setIfAbsent + expire 同上第二章示例
缓存(热点/冷数据) String/Hash + 过期策略 redisTemplate.opsForValue().set(key, val, timeout);
排行榜系统 ZSet 结构,打分 + 排序 redisTemplate.opsForZSet().incrementScore(key, item, 1);
计数器/限流器 incr、lua 脚本控制 redisTemplate.opsForValue().increment("count");
实时推荐/社交关注 Set/Hash/ZSet 组合 Set 存关注人列表,ZSet 存推荐分数

限流器 Lua 脚本示例(固定窗口)

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then
    return 0
else
    redis.call("INCR", key)
    redis.call("EXPIRE", key, 60)
    return 1
end

Java 中执行 Lua 脚本:

DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(luaScript);
script.setResultType(Long.class);
Long result = redisTemplate.execute(script, List.of("req:ip:127.0.0.1"), "10");

七、总结

Redis 是现代分布式系统中不可或缺的中间件,性能高、功能强、生态丰富。

  • 合理选择数据结构,是性能的核心保障
  • 针对缓存一致性与雪崩问题需做好应对机制
  • 配合 Spring Data Redis,开发效率高,易集成