第 4 课:Redis 高级特性

4.1 持久化机制

Redis 提供两种持久化方式,防止服务器重启后数据丢失:

RDB(Redis Database)

原理:

在指定的时间间隔内,将内存中的数据快照写入磁盘。

配置(redis.conf)

# 900秒内至少有1个键被修改,就触发快照
save 900 1

# 300秒内至少有10个键被修改,就触发快照
save 300 10

# 60秒内至少有10000个键被修改,就触发快照
save 60 10000

# 快照文件名
dbfilename dump.rdb

# 快照文件保存目录
dir ./

# 快照失败时停止接收写操作
stop-writes-on-bgsave-error yes

# 是否压缩快照文件
rdbcompression yes

优缺点

优点 缺点
恢复速度快 可能会丢失最后一次快照后的数据
文件体积小 大数据量时 fork 子进程可能造成短暂阻塞
对 Redis 性能影响较小 数据实时性不如 AOF

AOF(Append Only File)

原理:

以日志形式记录每个写操作,Redis 重启时重新执行日志中的命令恢复数据。

配置(redis.conf)

# 开启AOF
appendonly yes

# AOF文件名
appendfilename "appendonly.aof"

# 同步策略
# appendfsync always      # 每次写操作同步(最安全,最慢)
appendfsync everysec      # 每秒同步一次(推荐)
# appendfsync no          # 由操作系统决定(最快,最不安全)

# AOF重写配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

优缺点

优点 缺点
数据安全性高 文件体积通常比 RDB 大
最多丢失 1 秒数据 恢复速度慢于 RDB
支持 AOF Rewrite 压缩日志 对 Redis 性能有一定影响

RDB 与 AOF 对比

对比项 RDB AOF
数据安全性 较低 较高
恢复速度
文件大小
性能影响 较大
推荐用途 备份 数据恢复

最佳实践

  • 同时开启 RDB 和 AOF
  • RDB 用于数据备份
  • AOF 用于数据恢复
  • 定期执行 AOF Rewrite

4.2 Redis 事务

Redis 事务允许一次执行多个命令。

基本命令

# 开启事务
multi

# 命令入队
set key1 value1
set key2 value2
incr count

# 执行事务
exec

# 取消事务
discard

注意事项

  • Redis 事务不支持回滚
  • 某条命令失败不会影响其他命令执行
  • 可以结合 WATCH 实现乐观锁

WATCH 示例

# 监视count键
watch count

# 开启事务
multi

incr count

# 如果在exec之前count被其他客户端修改
# 当前事务执行失败
exec

4.3 发布订阅(Pub/Sub)

Redis 支持消息发布与订阅。

基本命令

# 订阅频道
subscribe channel1 channel2

# 发布消息
publish channel1 "hello world"

# 模式匹配订阅
psubscribe channel*

# 取消订阅
unsubscribe channel1

Java 示例

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class RedisMessageListener implements MessageListener {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String channel = new String(message.getChannel());
        String messageBody = new String(message.getBody());

        System.out.println(
            "收到消息:" + messageBody + ",来自频道:" + channel
        );
    }
}

应用场景

  • 消息广播
  • 系统通知
  • 实时聊天
  • 配置同步

4.4 Lua 脚本

Redis 支持 Lua 脚本,可以把多个命令组合成一个原子操作。

优势

优势 说明
原子执行 中途不会被打断
减少网络开销 一次请求执行多个命令
性能更高 服务端执行
适合复杂逻辑 分布式锁、限流等

基本命令

# 执行Lua脚本
eval "return redis.call('set', KEYS[1], ARGV[1])" 1 key1 value1

Java 示例:释放分布式锁

public boolean releaseLock(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";

    Long result =
            (Long) redisTemplate.execute(
                    (RedisCallback<Long>) connection ->
                            connection.eval(
                                    script.getBytes(),
                                    ReturnType.INTEGER,
                                    1,
                                    key.getBytes(),
                                    value.getBytes()
                            )
            );

    return Long.valueOf(1).equals(result);
}

Lua 脚本释放锁流程

获取锁
   ↓
保存唯一UUID
   ↓
执行Lua脚本
   ↓
比较UUID
   ↓
一致 → 删除锁
不一致 → 返回失败

本课知识总结

模块 核心内容
RDB 快照持久化
AOF 日志持久化
Transaction MULTI、EXEC、WATCH
Pub/Sub 发布订阅模式
Lua 原子脚本执行

课后练习

练习 1

配置 Redis 的:

  • RDB 持久化
  • AOF 持久化

测试 Redis 重启后的数据恢复能力。


练习 2

编写一个 Redis 事务,实现:

  • A 账户减 100
  • B 账户加 100

要求使用:

MULTI
EXEC

完成转账逻辑。


练习 3

使用 Lua 脚本实现:

  • 分布式锁释放逻辑
  • UUID 校验
  • 删除锁

保证原子性。


练习 4

实现一个简单发布订阅系统:

功能要求:

  • 发送消息
  • 订阅消息
  • 接收消息
  • 支持多个频道

推荐使用 Spring Boot + Redis 实现。