第 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 实现。