本文全面讲解 Redis 核心概念、数据类型、使用场景及持久化方案,适合初学者系统学习,也适合有经验的开发者查漏补缺。
一、概念与原理介绍
1.1 什么是 Redis?
Redis(Remote Dictionary Server) 是一个开源的、基于内存的键值对存储数据库,通常被称为数据结构服务器。它由 Salvatore Sanfilippo 于 2009 年开发,使用 ANSI C 语言编写。
核心特性
-
高性能
- 完全基于内存操作,读写速度极快
- 单线程模型(大部分情况下),避免了线程切换和锁的开销
- QPS 可达 10万+(简单操作)
-
丰富的数据结构
- 不仅仅是简单的 key-value 存储
- 支持字符串、哈希、列表、集合、有序集合等复杂数据结构
-
持久化机制
- RDB(Redis Database):定期将内存数据快照保存到磁盘
- AOF(Append Only File):记录写操作日志,实现增量持久化
-
分布式支持
- 主从复制
- 哨兵模式(Sentinel)
- 集群模式(Cluster)
-
原子性操作
- 所有操作都是原子性的
- 支持事务
1.2 Redis 与其他数据库的对比
| 特性 | Redis | MySQL | MongoDB | Memcached |
|---|---|---|---|---|
| 存储方式 | 内存 + 磁盘 | 磁盘 | 磁盘 + 内存 | 内存 |
| 数据模型 | 键值对 | 关系型 | 文档型 | 键值对 |
| 持久化 | 支持(RDB/AOF) | 支持 | 支持 | 不支持 |
| 数据结构 | 丰富 | 表结构 | BSON | 简单键值 |
| 读写速度 | 极快 | 较慢 | 中等 | 极快 |
| 适用场景 | 缓存、实时计算 | 事务、复杂查询 | 文档存储 | 简单缓存 |
核心区别:
- Redis 是内存优先的数据库,性能远超传统磁盘数据库
- 相比 Memcached,Redis 提供了更丰富的数据结构和持久化能力
- 相比传统数据库,Redis 是非关系型的,不使用表结构
1.3 Redis 的工作原理
单线程模型(Reactor 模式)
Redis 采用事件驱动的单线程模型:
┌─────────────────────────────────────┐
│ Event Loop (Reactor) │
├─────────────────────────────────────┤
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │Socket│ │Socket│ │Socket│ ... │
│ └──┬──┘ └──┬──┘ └──┬──┘ │
│ │ │ │ │
│ ┌──▼───────────────▼────────▼──┐ │
│ │ IO 多路复用 (epoll) │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌─────▼─────┐ │
│ │ 命令解析器 │ │
│ └─────┬─────┘ │
│ │ │
│ ┌─────▼─────┐ │
│ │ 命令执行器 │ (单线程处理) │
│ └─────┬─────┘ │
│ │ │
│ ┌─────▼─────┐ │
│ │ 响应返回器 │ │
│ └───────────┘ │
└─────────────────────────────────────┘
为什么单线程反而快?
- 避免了线程切换和锁竞争的开销
- 内存操作不需要多线程
- IO 多路复用技术处理并发连接
内存管理
Redis 使用自己的内存分配器:
- jemalloc:默认分配器(Linux)
- tcmalloc:可选
- 内存碎片管理:通过数据结构优化减少碎片
过期策略
Redis 使用两种策略处理过期 key:
-
被动过期
- 访问 key 时检查是否过期
- 过期则删除
-
主动过期
- 定期随机抽取 key 检查
- 删除已过期的 key
- 避免内存泄漏
二、基本数据类型详解
2.1 字符串(String)
特性
- 最基础的数据类型
- 可以存储任何类型的数据(字符串、数字、二进制数据等)
- 最大容量 512MB
- 原子性操作:INCR/DECR 等操作是线程安全的
常用命令
bash
# 基本操作
SET key value # 设置 key-value
GET key # 获取 key 对应的值
DEL key # 删除 key
EXISTS key # 判断 key 是否存在
# 过期时间
SETEX key seconds value # 设置 key 并指定过期时间(秒)
TTL key # 获取 key 的剩余过期时间
# 数字操作
INCR key # key 值加 1
DECR key # key 值减 1
INCRBY key increment # key 值增加指定数值
DECRBY key decrement # key 值减少指定数值
# 批量操作
MGET key1 key2 key3 # 批量获取多个 key 的值
MSET key1 val1 key2 val2 # 批量设置多个 key-value
# 字符串操作
APPEND key value # 在 key 对应的值后面追加内容
STRLEN key # 获取字符串长度
GETRANGE key start end # 获取字符串的子串
SETRANGE key offset value # 从指定位置开始设置字符串
使用示例
bash
# 设置用户信息
SET user:1:name "张三"
SET user:1:age 25
# 获取用户信息
GET user:1:name # 返回 "张三"
GET user:1:age # 返回 "25"
# 计数器示例
SET article:1001:views 0
INCR article:1001:views # 返回 1
INCRBY article:1001:views 10 # 返回 11
# 设置带过期时间的验证码
SETEX verify:phone:13800138000 300 "123456" # 5分钟过期
TTL verify:phone:13800138000 # 查看剩余时间
应用场景
- 缓存
- 计数器(文章阅读量、点赞数等)
- Session 存储
- 分布式锁
2.2 哈希(Hash)
特性
- 存储键值对集合
- 类似于 Java 的 HashMap 或 Python 的字典
- 适合存储对象信息
- 每个哈希可以存储 2³² - 1 个键值对(约 40 亿)
常用命令
bash
# 基本操作
HSET key field value # 设置哈希表 key 中字段 field 的值
HGET key field # 获取哈希表 key 中字段 field 的值
HMSET key field1 val1 field2 val2 # 批量设置
HMGET key field1 field2 # 批量获取
HGETALL key # 获取哈希表中所有字段和值
HDEL key field1 field2 # 删除一个或多个字段
HEXISTS key field # 查看字段是否存在
# 字段操作
HKEYS key # 获取哈希表中所有字段名
HVALS key # 获取哈希表中所有字段值
HLEN key # 获取哈希表中字段数量
# 数值操作
HINCRBY key field increment # 为哈希表中的字段增加指定数值
HINCRBYFLOAT key field increment # 浮点数增加
使用示例
bash
# 存储用户信息
HSET user:1 name "张三"
HSET user:1 age 25
HSET user:1 city "北京"
HSET user:1 email "zhangsan@example.com"
# 获取用户信息
HGET user:1 name # 返回 "张三"
HGET user:1 age # 返回 "25"
# 获取所有用户信息
HGETALL user:1
# 返回:
# 1) "name"
# 2) "张三"
# 3) "age"
# 4) "25"
# 5) "city"
# 6) "北京"
# 7) "email"
# 8) "zhangsan@example.com"
# 更新用户年龄
HINCRBY user:1 age 1 # 年龄加 1,返回 26
# 获取所有字段
HKEYS user:1 # 返回所有字段名
# 获取字段数量
HLEN user:1 # 返回 4
应用场景
- 存储对象(用户信息、商品信息等)
- 购物车(key 为用户ID,field 为商品ID,value 为数量)
- 配置信息存储
2.3 列表(List)
特性
- 存储有序的字符串列表
- 类似于 Java 的 LinkedList
- 可以在头部或尾部添加/删除元素
- 支持双向操作
- 最多存储 2³² - 1 个元素
常用命令
bash
# 添加元素
LPUSH key value1 value2 # 在列表头部插入一个或多个值
RPUSH key value1 value2 # 在列表尾部插入一个或多个值
# 获取元素
LPOP key # 移除并获取列表头部的元素
RPOP key # 移除并获取列表尾部的元素
LRANGE key start stop # 获取列表指定范围内的元素(索引从 0 开始)
# 其他操作
LLEN key # 获取列表长度
LINDEX key index # 获取列表中指定索引的元素
LTRIM key start stop # 修剪列表,只保留指定区间内的元素
LINSERT key BEFORE|AFTER pivot value # 在元素 pivot 前或后插入值
使用示例
bash
# 创建消息队列
LPUSH messages "消息1" "消息2" "消息3"
# 列表变为: ["消息3", "消息2", "消息1"]
# 获取消息
RPOP messages # 返回 "消息1"
RPOP messages # 返回 "消息2"
# 查看所有消息
LRANGE messages 0 -1 # -1 表示最后一个元素
# 最新动态列表(微博、推特等)
LPUSH user:1:timeline "发布了新文章"
LPUSH user:1:timeline "点赞了评论"
LPUSH user:1:timeline "关注了新用户"
# 获取最新 10 条动态
LRANGE user:1:timeline 0 9
# 列表长度
LLEN user:1:timeline # 返回 3
# 获取指定位置的动态
LINDEX user:1:timeline 0 # 返回 "关注了新用户"
应用场景
- 消息队列(生产者-消费者模式)
- 最新动态(Timeline、时间轴)
- 栈(LPOP/LPUSH)和队列(RPOP/LPUSH)
- 文章评论列表
2.4 集合(Set)
特性
- 存储无序、不重复的字符串集合
- 类似于 Java 的 HashSet
- 支持集合运算(并集、交集、差集)
- 最多存储 2³² - 1 个元素
常用命令
bash
# 基本操作
SADD key member1 member2 # 向集合添加一个或多个成员
SMEMBERS key # 获取集合中的所有成员
SISMEMBER key member # 判断 member 是否是集合的成员
SREM key member1 member2 # 移除集合中的一个或多个成员
SCARD key # 获取集合的成员数
# 集合运算
SUNION key1 key2 # 返回所有给定集合的并集
SINTER key1 key2 # 返回所有给定集合的交集
SDIFF key1 key2 # 返回所有给定集合的差集(key1 - key2)
# 随机操作
SRANDMEMBER key [count] # 返回集合中一个或多个随机成员
SPOP key # 移除并返回集合中的一个随机成员
使用示例
bash
# 标签系统
SADD article:1001:tags "Redis" "数据库" "缓存" "技术"
SADD article:1002:tags "MySQL" "数据库" "关系型"
SADD article:1003:tags "Redis" "缓存"
# 查看文章的所有标签
SMEMBERS article:1001:tags
# 共同关注(交集)
SADD user:1:following "用户A" "用户B" "用户C"
SADD user:2:following "用户B" "用户C" "用户D"
SINTER user:1:following user:2:following
# 返回: ["用户B", "用户C"]
# 推荐系统(差集)
# 找出用户1关注但用户2未关注的用户
SDIFF user:1:following user:2:following
# 返回: ["用户A"]
# 随机抽取获奖用户
SADD lottery:2024 "用户1" "用户2" "用户3" "用户4" "用户5"
SPOP lottery:2024 # 随机移除一个用户并返回
应用场景
- 标签系统
- 共同好友/共同关注
- 推荐系统(基于共同兴趣)
- 去重(如 UV 统计)
- 抽奖系统
2.5 有序集合(Sorted Set)
特性
- 集合中每个成员都关联一个分数(Score)
- 根据分数自动排序
- 分数可以重复,成员不可重复
- 最多存储 2³² - 1 个元素
常用命令
bash
# 基本操作
ZADD key score1 member1 score2 member2 # 向有序集合添加一个或多个成员
ZREM key member1 member2 # 移除有序集合中的一个或多个成员
ZSCORE key member # 获取成员的分数
# 范围查询
ZRANGE key start stop [WITHSCORES] # 返回有序集合中指定区间内的成员(按分数升序)
ZREVRANGE key start stop [WITHSCORES] # 返回有序集合中指定区间内的成员(按分数降序)
ZRANGEBYSCORE key min max [WITHSCORES] # 返回分数在指定区间内的成员
ZREVRANGEBYSCORE key max min [WITHSCORES] # 降序返回
# 排名查询
ZRANK key member # 返回成员在集合中的排名(升序,从 0 开始)
ZREVRANK key member # 返回成员在集合中的排名(降序,从 0 开始)
# 统计操作
ZCARD key # 获取有序集合的成员数
ZCOUNT key min max # 计算分数在指定区间内的成员数量
ZINCRBY key increment member # 有序集合中对指定成员的分数加上增量
# 删除操作
ZREMRANGEBYRANK key start stop # 移除指定排名区间内的所有成员
ZREMRANGEBYSCORE key min max # 移除分数在指定区间内的所有成员
使用示例
bash
# 排行榜系统
ZADD leaderboard 95 "玩家A" 87 "玩家B" 92 "玩家C" 88 "玩家D" 90 "玩家E"
# 获取前 10 名
ZREVRANGE leaderboard 0 9 WITHSCORES
# 返回:
# 1) "玩家A"
# 2) "95"
# 3) "玩家C"
# 4) "92"
# 5) "玩家E"
# 6) "90"
# ...
# 获取玩家排名
ZREVRANK leaderboard "玩家C" # 返回 1(排名第2,从0开始)
ZREVRANK leaderboard "玩家B" # 返回 4(排名第5)
# 获取玩家分数
ZSCORE leaderboard "玩家A" # 返回 "95"
# 分数区间查询(获取分数在 90-100 之间的玩家)
ZRANGEBYSCORE leaderboard 90 100 WITHSCORES
# 玩家得分更新
ZINCRBY leaderboard 5 "玩家D" # 玩家D分数增加5,从88变成93
# 实时新闻热度榜
ZADD news:hot:20240204 1000 "新闻A" 800 "新闻B" 1200 "新闻C"
# 每次用户点击新闻,增加热度
ZINCRBY news:hot:20240204 1 "新闻A"
# 获取热度最高的 5 条新闻
ZREVRANGE news:hot:20240204 0 4
应用场景
- 排行榜(游戏积分榜、文章热度榜等)
- 延时队列(使用分数作为时间戳)
- 优先级队列
- 范围查询(按分数范围获取数据)
- 实时统计(如点击量、热度)
2.6 其他数据类型简介
Bitmap(位图)
- 本质上是字符串,但按位操作
- 用于存储二进制状态
- 命令:SETBIT, GETBIT, BITCOUNT, BITOP
- 应用:用户签到、在线状态等
HyperLogLog
- 基数统计算法
- 用于统计不重复元素的数量
- 内存占用极少(12KB)
- 应用:UV 统计、日活统计
Geo(地理位置)
- 存储地理位置信息
- 支持距离计算、范围查询
- 命令:GEOADD, GEODIST, GEORADIUS
- 应用:附近的人、地图服务
Stream(流)
- Redis 5.0 新增
- 消息队列的高级实现
- 支持消费者组
- 应用:日志收集、实时数据处理
三、使用场景分析
3.1 缓存
场景描述
将热点数据存储在 Redis 中,减少数据库压力,提高响应速度。
实现方式
bash
# 缓存用户信息
# 1. 先查 Redis
GET user:info:1001
# 2. 如果 Redis 没有,查询数据库,然后写入 Redis
SET user:info:1001 '{"name":"张三","age":25}'
EXPIRE user:info:1001 3600 # 设置1小时过期
# 或者使用 SETEX 一步完成
SETEX user:info:1001 3600 '{"name":"张三","age":25}'
优点
- 性能提升:内存读取速度远超磁盘数据库
- 减少数据库压力:大量读请求由 Redis 处理
- 降低成本:相比扩容数据库,使用 Redis 更经济
注意事项
- 缓存穿透 :查询不存在的数据,绕过缓存直击数据库
- 解决方案:布隆过滤器、缓存空值
- 缓存雪崩 :大量缓存同时失效
- 解决方案:随机过期时间、缓存预热
- 缓存击穿 :热点 key 突然失效
- 解决方案:互斥锁、永不过期
3.2 会话存储
场景描述
在分布式系统中,将用户 Session 信息存储在 Redis 中,实现会话共享。
实现方式
bash
# 用户登录后,存储 Session
HSET session:abc123 user_id 1001
HSET session:abc123 username "张三"
HSET session:abc123 login_time 1707048000
EXPIRE session:abc123 7200 # 2小时过期
# 验证用户是否登录
HEXISTS session:abc123 user_id # 返回 1 表示已登录
# 获取用户信息
HGET session:abc123 username # 返回 "张三"
# 用户登出
DEL session:abc123
优点
- 跨服务共享:多个服务实例可以共享 Session
- 快速验证:Redis 响应速度快
- 自动过期:设置 TTL 自动清理过期 Session
适用场景
- 分布式 Web 应用
- 移动端应用
- 微服务架构
3.3 计数器
场景描述
统计文章阅读量、点赞数、商品销量等需要频繁更新的数值。
实现方式
bash
# 文章阅读量
SET article:1001:views 0
INCR article:1001:views # 每次阅读加 1
# 批量增加(如从其他数据源同步)
INCRBY article:1001:views 1000
# 获取阅读量
GET article:1001:views
# 点赞数(使用 Hash 存储多个维度)
HSET article:1001:stats views 10000
HSET article:1001:stats likes 500
HSET article:1001:stats comments 50
# 点赞操作
HINCRBY article:1001:stats likes 1
# 获取所有统计信息
HGETALL article:1001:stats
优点
- 原子性操作:INCR 等操作是原子的,保证数据准确性
- 高性能:支持高并发计数
- 持久化:数据不会丢失(如果开启持久化)
适用场景
- 文章阅读量/点赞数
- 视频播放量
- 商品销量统计
- 日活/月活统计
3.4 排行榜
场景描述
游戏积分榜、文章热度榜、粉丝数排行等需要实时排序的场景。
实现方式
bash
# 游戏积分榜
ZADD game:leaderboard 10000 "玩家A" 9500 "玩家B" 8800 "玩家C" 9200 "玩家D"
# 玩家得分后更新
ZINCRBY game:leaderboard 100 "玩家C" # 玩家C增加100分
# 获取前 10 名
ZREVRANGE game:leaderboard 0 9 WITHSCORES
# 获取自己的排名
ZREVRANK game:leaderboard "玩家C" # 返回排名
# 获取特定分数区间的玩家
ZRANGEBYSCORE game:leaderboard 9000 10000 WITHSCORES
# 文章热度榜(基于点赞数)
ZADD article:hot:20240204 100 "文章A" 200 "文章B" 150 "文章C"
# 每次点赞,增加热度
ZINCRBY article:hot:20240204 1 "文章A"
# 获取最热文章
ZREVRANGE article:hot:20240204 0 4
优点
- 实时更新:分数变化后立即反映在排名中
- 范围查询:可以查询特定排名或分数区间的数据
- 高性能:即使有大量数据,查询依然快速
适用场景
- 游戏积分榜
- 文章热度榜
- 商品销量排行
- 粉丝数排行
3.5 消息队列
场景描述
使用 List 实现简单的消息队列,实现生产者-消费者模式。
实现方式
bash
# 生产者:发送消息
LPUSH mq:tasks "任务1:发送邮件"
LPUSH mq:tasks "任务2:生成报表"
LPUSH mq:tasks "任务3:处理图片"
# 消费者:获取并处理消息
RPOP mq:tasks # 返回 "任务1:发送邮件"
# 使用 BRPOP 实现阻塞式获取(无消息时阻塞等待)
BRPOP mq:tasks 30 # 最多等待 30 秒
优点
- 简单易用:Redis List 天然支持队列操作
- 高性能:基于内存,速度快
- 持久化:消息不会丢失(如果开启持久化)
局限性
- 不支持消息确认机制
- 不支持复杂的消息路由
- 不支持多个消费者组
高级替代方案:
- Redis Stream(支持消费者组)
- 专业的消息队列:RabbitMQ、Kafka 等
适用场景
- 简单的任务队列
- 异步处理(如发送邮件、生成报表)
- 延时任务(配合有序集合)
3.6 分布式锁
场景描述
在分布式系统中,防止多个进程同时操作同一资源。
实现方式
bash
# 加锁(使用 SET 命令一步完成)
SET lock:resource:1 "unique_value" NX EX 30
# NX:只在 key 不存在时设置
# EX:设置过期时间(秒)
# 解锁(确保只有锁的持有者才能解锁)
# Lua 脚本保证原子性
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
优点
- 简单易用:只需几条命令即可实现
- 高性能:基于内存,加锁/解锁速度快
- 自动过期:避免死锁
注意事项
- 需要设置唯一的 value,确保只能释放自己的锁
- 建议使用 Redisson 等成熟的客户端库
- 对于高可靠性场景,建议使用 Redlock 算法
适用场景
- 限时抢购
- 库存扣减
- 分布式任务调度
3.7 其他实用场景
标签系统(使用 Set)
bash
# 添加标签
SADD user:1001:tags "程序员" "游戏玩家" "电影迷"
# 查找共同标签的用户
SINTER user:1001:tags user:1002:tags
限流(使用字符串 + 过期时间)
bash
# 每分钟限制 100 次请求
INCR limit:api:1001:202402041028
EXPIRE limit:api:1001:202402041028 60
# 检查是否超限
GET limit:api:1001:202402041028
# 如果返回值 > 100,则拒绝请求
最近浏览(使用 List)
bash
# 添加浏览记录
LPUSH recent:user:1001 "商品1"
LPUSH recent:user:1001 "商品2"
LPUSH recent:user:1001 "商品3"
# 只保留最近 10 条
LTRIM recent:user:1001 0 9
# 获取最近浏览
LRANGE recent:user:1001 0 -1
在线状态(使用 Bitmap)
bash
# 记录用户在线状态(按位存储,每个 bit 代表一个用户)
SETBIT online:20240204 1001 1 # 用户 1001 在线
SETBIT online:20240204 1002 0 # 用户 1002 离线
# 统计在线人数
BITCOUNT online:20240204
# 检查用户是否在线
GETBIT online:20240204 1001 # 返回 1 表示在线
四、Redis 持久化方案深度解析
Redis 提供了两种主要的持久化方案:RDB 和 AOF,以及两者的混合模式。正确选择持久化方案是 Redis 架构设计的核心决策点。
4.1 RDB(Redis Database)
工作原理
RDB 是 Redis 默认的持久化方式,它通过定期生成内存数据快照 并保存到磁盘上的 .rdb 文件来实现持久化。
fork() → 子进程遍历内存数据 → 写入临时文件 → 原子性替换旧的 RDB 文件
优点
- 文件紧凑:二进制格式,文件体积小,适合备份和传输
- 恢复极快:服务重启时直接加载到内存,无需解析
- 对性能影响小:子进程完成持久化,主进程几乎不阻塞
- 适合灾难恢复:文件独立,可异地备份
缺点
- 数据丢失风险:最后一次快照后的数据会丢失(默认可能丢失 15 分钟数据)
- fork 开销:大数据量时 fork 会卡顿(毫秒到秒级)
- 不实时:无法做到秒级持久化
配置示例
bash
# RDB 快照规则
save 900 1 # 900 秒内至少 1 个 key 变化时触发
save 300 10 # 300 秒内至少 10 个 key 变化时触发
save 60 10000 # 60 秒内至少 10000 个 key 变化时触发
# 其他配置
rdbcompression yes # 压缩 RDB 文件
rdbchecksum yes # 启用 CRC64 校验
rdbfilename dump.rdb # RDB 文件名
dir /var/lib/redis # 文件存储目录
最佳使用场景
- ✅ 数据可容忍分钟级丢失(缓存、临时数据)
- ✅ 需要快速备份和恢复
- ✅ 定时备份到远程存储
4.2 AOF(Append Only File)
工作原理
AOF 通过记录所有写操作命令来实现持久化,类似于数据库的 WAL(Write-Ahead Log)。
写命令 → 写入 AOF 缓冲区 → 写入磁盘(根据 fsync 策略)→ AOF 重写
三种 fsync 策略(核心差异)
| 策略 | 描述 | 性能影响 | 数据安全 |
|---|---|---|---|
| always | 每次写都 fsync | 最低(性能下降 10-100 倍) | 最高(不丢数据) |
| everysec | 每秒 fsync(默认) | 较好 | 最多丢 1 秒数据 |
| no | 由操作系统决定 fsync | 最高 | 可能丢失更多数据 |
优点
- 数据安全:最多丢失 1 秒数据(everysec 策略)
- 可读性强:AOF 文件是文本格式,可人工查看和修复
- 自动重写:防止文件过大(当 AOF 文件超过上次重写后的一定比例时触发)
- 容错性好:Redis 启动时自动修复损坏的 AOF 文件
缺点
- 文件大:通常比 RDB 大 2-3 倍
- 恢复慢:需要重放所有命令,恢复速度较慢
- 性能影响大:频繁的磁盘写入影响性能
配置示例
bash
# 启用 AOF
appendonly yes
appendfilename "appendonly.aof"
# fsync 策略
appendfsync everysec # 推荐:每秒同步一次
# AOF 重写配置
auto-aof-rewrite-percentage 100 # 比上次重写后增长 100%
auto-aof-rewrite-min-size 64mb # 最小 64MB 才触发重写
# AOF 加载恢复时,是否忽略最后可能损坏的命令
aof-load-truncated yes
最佳使用场景
- ✅ 数据不能丢失(如账户余额、订单数据)
- ✅ 需要审计能力(可查看历史操作记录)
- ✅ 对数据完整性要求高
4.3 混合持久化(Redis 4.0+)
工作原理
混合持久化结合了 RDB 和 AOF 的优点:AOF 重写时,子进程将内存数据以 RDB 格式写入 AOF 文件开头,主进程将重写期间的新命令追加到文件末尾。
AOF 文件结构:
[RDB 格式的数据快照] + [重写期间新增的 AOF 命令]
优势
- 文件比纯 AOF 小
- 恢复速度比纯 AOF 快
- 数据安全性接近纯 AOF
配置示例
bash
aof-use-rdb-preamble yes # 启用混合持久化
推荐:生产环境优先开启。
4.4 持久化方案对比总结
| 方案 | 本质 | 写入方式 | 恢复速度 | 数据完整性 | 性能影响 | 文件大小 |
|---|---|---|---|---|---|---|
| RDB | 快照 | 间隔性全量写入 | 极快 | 可能丢失数秒到数分钟 | 低(fork 时有阻塞) | 小 |
| AOF | 日志 | 实时追加写 | 较慢 | 最少丢失 1 秒数据 | 高(频繁写磁盘) | 大 |
| 混合持久化 | 快照+日志 | RDB+增量AOF | 较快 | 最少丢失 1 秒数据 | 中 | 中 |
4.5 持久化方案选择决策树
你的数据能容忍丢失吗?
│
├─ 能接受分钟级丢失 → RDB 单独使用
│ (推荐场景:缓存、临时数据)
│
└─ 不能接受丢失(或最多 1 秒)
│
├─ 对性能要求极高 → RDB + AOF(fsync=no)
│ (风险:可能丢失较多数据)
│
├─ 平衡性能与安全 → RDB + AOF(fsync=everysec)+ 混合持久化
│ (推荐:生产环境标准配置)
│
└─ 数据绝对安全 → AOF(fsync=always)
(代价:性能下降 10-100 倍,慎重)
4.6 生产环境推荐配置
通用推荐(平衡性能与安全)
bash
# RDB 配置
save 900 1
save 300 10
save 60 10000
# AOF 配置
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes # 混合持久化
极致性能(可接受少量数据丢失)
bash
# 仅使用 RDB
save 900 1
save 300 10
save 60 10000
appendonly no
极致安全(数据不可丢失)
bash
# 仅使用 AOF,fsync=always
appendonly yes
appendfsync always
# 可选:同时启用 RDB 用于备份
save 900 1
4.7 持久化实战注意事项
主从复制的持久化策略
- 主节点:建议启用 AOF,保证数据安全
- 从节点:可以使用 RDB 或不持久化,从主节点同步数据即可
- 原因:从节点宕机不影响主节点数据
监控指标
bash
# 查看持久化状态
INFO persistence
# 关注指标:
# rdb_last_bgsave_time_sec:上次 RDB 耗时
# aof_last_rewrite_time_sec:上次 AOF 重写耗时
# aof_rewrite_in_progress:是否正在重写
灾难恢复流程
- 停止 Redis 服务
- 检查 RDB 和 AOF 文件完整性
- 优先使用 AOF 恢复(更完整)
- 若 AOF 损坏,尝试使用
redis-check-aof修复 - 若修复失败,使用 RDB 恢复
- 启动 Redis,验证数据
4.8 常见问题
Q1:RDB 和 AOF 同时开启,哪个优先?
A: 重启时优先使用 AOF 文件恢复(因为它更完整)。
Q2:fork 卡顿怎么解决?
A:
- 调整
repl-backlog-size增大缓冲区 - 使用无 fork 持久化(Redis 6.0+ 的无 fork AOF)
- 拆分实例,减少单个实例数据量
Q3:AOF 文件太大怎么办?
A:
- 自动重写会触发(
auto-aof-rewrite-percentage) - 手动执行
BGREWRITEAOF - 开启混合持久化
Q4:如何禁用持久化?
A: 将 save "" 注释所有 RDB 规则,appendonly no 禁用 AOF。仅用于纯缓存场景。
五、实战练习
练习 1:构建简单的用户缓存系统
目标:实现用户信息的缓存,包含设置、获取、过期操作。
bash
# 1. 设置用户信息
HSET user:1001 name "张三"
HSET user:1001 age 25
HSET user:1001 email "zhangsan@example.com"
# 2. 设置过期时间(1小时)
EXPIRE user:1001 3600
# 3. 获取用户信息
HGET user:1001 name
# 4. 检查用户是否存在
EXISTS user:1001
# 5. 查看剩余过期时间
TTL user:1001
练习 2:实现文章阅读量统计
目标:统计文章的阅读量和点赞数。
bash
# 1. 初始化文章统计
HSET article:1001 stats:views 0
HSET article:1001 stats:likes 0
HSET article:1001 stats:comments 0
# 2. 文章被阅读
HINCRBY article:1001 stats:views 1
# 3. 文章被点赞
HINCRBY article:1001 stats:likes 1
# 4. 获取文章统计数据
HGETALL article:1001
# 5. 获取阅读量
HGET article:1001 stats:views
练习 3:构建游戏排行榜
目标:实现实时游戏积分榜,支持分数更新和排名查询。
bash
# 1. 添加玩家积分
ZADD game:leaderboard 1000 "玩家A" 950 "玩家B" 880 "玩家C" 920 "玩家D"
# 2. 玩家得分
ZINCRBY game:leaderboard 50 "玩家C"
# 3. 获取前 3 名
ZREVRANGE game:leaderboard 0 2 WITHSCORES
# 4. 获取自己的排名
ZREVRANK game:leaderboard "玩家B"
# 5. 获取积分在 900-1000 之间的玩家
ZRANGEBYSCORE game:leaderboard 900 1000 WITHSCORES
练习 4:实现简单的消息队列
目标:使用 List 实现生产者-消费者模式的消息队列。
bash
# 生产者:发送任务
LPUSH task:queue "发送邮件给用户1001"
LPUSH task:queue "生成月度报表"
LPUSH task:queue "处理图片压缩"
# 消费者:处理任务
RPOP task:queue # 获取并移除任务
# 查看队列中剩余任务数
LLEN task:queue
# 查看所有待处理任务
LRANGE task:queue 0 -1
六、总结与最佳实践
6.1 Redis 的优势总结
- 极致性能:基于内存,响应速度快,QPS 高
- 丰富数据结构:支持多种数据类型,满足不同场景需求
- 原子性操作:保证数据一致性
- 持久化支持:数据不会因服务重启而丢失
- 分布式支持:主从复制、哨兵、集群保证高可用
- 简单易用:命令简单直观,学习成本低
6.2 最佳实践
Key 命名规范
bash
# 使用冒号分隔
user:1001:info
article:1001:views
session:abc123
# 使用有意义的名称,避免过长
# ❌ bad
# ✅ good
内存优化
- 选择合适的数据结构
- 及时设置过期时间
- 使用压缩列表(List、Hash、Set、ZSet 的内部编码)
性能优化
- 使用 Pipeline 批量操作
- 避免大 key(单个 key 的 value 过大)
- 使用 Lua 脚本实现原子性复杂操作
- 合理配置 maxmemory 和淘汰策略
安全性
- 设置密码(requirepass)
- 禁用危险命令(FLUSHALL、FLUSHDB、KEYS 等)
- 使用 ACL(访问控制列表)
- 网络隔离(防火墙)
持久化选择
| 场景 | 推荐方案 |
|---|---|
| 生产环境标准配置 | RDB + AOF(everysec)+ 混合持久化 |
| 纯缓存(数据可丢) | RDB 单独使用或完全不持久化 |
| 金融/订单等核心数据 | AOF(everysec 或 always) |
| 备份/灾难恢复 | RDB 定期备份到远程 |
附录:常用命令速查表
| 命令 | 描述 |
|---|---|
| 通用命令 | |
SET key value |
设置 key-value |
GET key |
获取 key 的值 |
DEL key |
删除 key |
EXISTS key |
判断 key 是否存在 |
EXPIRE key seconds |
设置过期时间 |
TTL key |
获取剩余过期时间 |
| 字符串 | |
INCR key |
值加 1 |
DECR key |
值减 1 |
INCRBY key increment |
值增加指定数值 |
APPEND key value |
追加字符串 |
| 哈希 | |
HSET key field value |
设置字段 |
HGET key field |
获取字段值 |
HGETALL key |
获取所有字段 |
HINCRBY key field inc |
字段值增加 |
| 列表 | |
LPUSH key value |
头部插入 |
RPUSH key value |
尾部插入 |
LPOP key |
头部弹出 |
RPOP key |
尾部弹出 |
LRANGE key start stop |
获取范围 |
| 集合 | |
SADD key member |
添加成员 |
SMEMBERS key |
获取所有成员 |
SISMEMBER key member |
判断成员是否存在 |
SUNION key1 key2 |
并集 |
SINTER key1 key2 |
交集 |
| 有序集合 | |
ZADD key score member |
添加成员 |
ZRANGE key start stop |
获取范围(升序) |
ZREVRANGE key start stop |
获取范围(降序) |
ZINCRBY key inc member |
分数增加 |
ZSCORE key member |
获取分数 |
ZREVRANK key member |
获取排名(降序) |
结语
Redis 的强大之处不仅在于其高性能,更在于其丰富的数据结构能够优雅地解决各种复杂业务场景。持续学习和实践,你将能够在实际项目中游刃有余地运用 Redis。