Redis 知识点与应用场景
1. Redis 简介与核心特性
Redis(Remote Dictionary Server)是一款开源的内存数据存储系统,支持多种数据结构,兼具高性能、持久化、分布式等特性,广泛用于缓存、数据库、消息中间件等场景。其核心特性包括:
- 高性能:基于内存操作,单线程模型避免上下文切换开销,QPS 可达 10 万 +(取决于数据大小和命令复杂度);
- 多数据结构支持:不仅支持基础的键值对,还提供 String、Hash、List 等丰富数据类型;
- 持久化:通过 RDB 和 AOF 机制将内存数据持久化到磁盘,防止数据丢失;
- 高可用:支持主从复制、哨兵(Sentinel)实现故障自动转移;
- 分布式:Redis Cluster 提供分片存储,突破单机内存限制;
- 功能丰富:支持事务、发布订阅、Lua 脚本、地理空间等高级特性。
2. 核心数据类型及应用场景详解
Redis 提供8 种核心数据类型,每种类型针对特定场景优化,以下为详细说明:
2.1 String(字符串)
数据结构特点
- 二进制安全:可存储文本、图片、序列化对象等任意二进制数据;
- 最大容量:单个 String 值最大 512MB;
- 底层实现:小字符串(<39 字节)用 SDS(简单动态字符串),大字符串用 embstr/raw 编码。
常用命令
命令 | 功能描述 | 示例 |
---|---|---|
SET key value [EX seconds] | 设置键值对,可选过期时间 | SET user:1001 '{"name":"Alice","age":25}' EX 3600 (缓存 1 小时) |
GET key | 获取键值 | GET user:1001 → 返回用户信息 JSON |
INCR key | 整数自增 1(原子操作) | INCR article:read:10086 (文章阅读量 + 1) |
DECR key | 整数自减 1(原子操作) | DECR stock:iphone15 (库存 - 1) |
APPEND key value | 追加字符串到值末尾 | APPEND log:user:1001 " login success" |
应用场景
缓存热点数据:存储用户信息、商品详情等高频访问数据,减轻数据库压力。
示例:电商场景中,缓存商品详情页数据:SET product:10001 '{"id":10001,"name":"无线耳机","price":999}' EX 1800
(缓存 30 分钟),用户访问时直接从 Redis 获取,无需查询 MySQL。计数器:利用
INCR
/DECR
的原子性,实现文章阅读量、视频播放量、接口请求次数统计。
示例:短视频平台统计视频播放量:用户每次观看视频时,执行INCR video:play:9527
,后台定期将计数器值同步到数据库。分布式锁:基于
SET key value NX EX
命令(NX:不存在才设置,EX:过期时间)实现分布式锁,防止并发操作冲突。
示例:秒杀场景中,通过SET lock:seckill:1001 "1" NX EX 10
抢占锁,获取锁后执行库存扣减,执行完毕释放锁(DEL lock:seckill:1001
)。
2.2 Hash(哈希)
数据结构特点
- 键值对集合:一个 Hash 键包含多个
field-value
键值对,类似 JSON 对象; - 空间高效:适合存储对象,可单独操作字段(无需整体更新);
- 底层实现:小 Hash(field 数量少且值小)用压缩列表,大 Hash 用哈希表。
常用命令
命令 | 功能描述 | 示例 |
---|---|---|
HSET key field value | 设置 Hash 字段值 | HSET user:1001 name "Alice" age 25 email "alice@example.com" |
HGET key field | 获取 Hash 字段值 | HGET user:1001 name → "Alice" |
HMGET key field1 field2 | 批量获取 Hash 字段值 | HMGET user:1001 name age → ["Alice", "25"] |
HGETALL key | 获取 Hash 所有字段和值 | HGETALL user:1001 → ["name","Alice","age","25",...] |
HINCRBY key field num | 字段值整数自增 num(原子操作) | HINCRBY user:1001 score 10 (用户积分 + 10) |
应用场景
对象存储:存储用户、商品等结构化数据,支持字段级更新,比 String 更灵活。
示例:社交平台存储用户资料:HSET user:1001 name "Bob" avatar "avatar.jpg" vip_level 3
,更新头像时仅需HSET user:1001 avatar "new_avatar.jpg"
,无需修改整个对象。购物车:以用户 ID 为 Hash 键,商品 ID 为 field,数量为 value,实现购物车增删改查。
示例:用户 1001 的购物车:HSET cart:1001 2001 2 2002 1
(商品 2001 数量 2,商品 2002 数量 1),修改数量时HSET cart:1001 2001 3
,清空购物车时DEL cart:1001
。
2.3 List(列表)
数据结构特点
- 有序字符串列表:元素按插入顺序排列,支持两端插入 / 删除;
- 双向链表特性:可充当栈(LIFO)或队列(FIFO);
- 底层实现:小 List(元素少且小)用压缩列表,大 List 用双向链表。
常用命令
命令 | 功能描述 | 示例 |
---|---|---|
LPUSH key value1 [value2] | 从列表左侧插入元素 | LPUSH msg:queue "msg1" "msg2" (左侧插入两条消息) |
RPUSH key value1 [value2] | 从列表右侧插入元素 | RPUSH article:latest 1001 1002 (右侧追加文章 ID) |
LPOP key | 从列表左侧弹出元素(返回并删除) | LPOP msg:queue → "msg2"(栈模式:后进先出) |
RPOP key | 从列表右侧弹出元素(返回并删除) | RPOP msg:queue → "msg1"(队列模式:先进先出) |
LRANGE key start stop | 获取列表指定范围元素(0 为头,-1 为尾) | LRANGE article:latest 0 9 → 获取最新 10 篇文章 ID |
应用场景
消息队列:基于
LPUSH+RPOP
实现简单的 FIFO 消息队列,生产者左侧插入消息,消费者右侧弹出消息。
示例:订单系统异步通知:订单创建后,LPUSH order:notify "order123"
,通知服务循环执行RPOP order:notify
获取订单 ID 并发送短信。最新数据展示:基于
LPUSH+LRANGE
实现 “最新 N 条数据” 功能,如最新文章、最近评论。
示例:博客平台展示最新 5 篇文章:发布文章时LPUSH article:latest 3001
(文章 ID),前端调用LRANGE article:latest 0 4
获取前 5 篇文章 ID,再查询详情。
2.4 Set(集合)
数据结构特点
- 无序字符串集合:元素唯一(自动去重),不保证顺序;
- 支持集合运算:交集、并集、差集,时间复杂度 O (N);
- 底层实现:小 Set 用整数集合(元素为整数且少),大 Set 用哈希表。
常用命令
命令 | 功能描述 | 示例 |
---|---|---|
SADD key member1 member2 | 向集合添加元素(自动去重) | SADD user:tags:1001 "tech" "music" "sports" (用户 1001 的标签) |
SMEMBERS key | 获取集合所有元素 | SMEMBERS user:tags:1001 → ["tech","music","sports"] |
SISMEMBER key member | 判断元素是否在集合中 | SISMEMBER user:tags:1001 "music" → 1(存在) |
SINTER key1 key2 | 计算两个集合的交集 | SINTER user:friends:1001 user:friends:1002 (共同好友) |
SUNION key1 key2 | 计算两个集合的并集 | SUNION user:tags:1001 user:tags:1002 (合并标签) |
SREM key member | 从集合删除元素 | SREM user:tags:1001 "sports" (移除标签) |
应用场景
标签系统:存储用户兴趣标签、文章分类等,支持标签添加、删除、查询。
示例:内容平台用户标签管理:用户 1001 添加 “科技”“音乐” 标签后,可通过SMEMBERS user:tags:1001
获取所有标签,推荐内容时根据标签匹配。共同好友 / 兴趣:通过
SINTER
计算交集,实现社交平台 “共同好友” 功能。
示例:用户 A(ID1001)和用户 B(ID1002)的好友列表分别为user:friends:1001
和user:friends:1002
,执行SINTER user:friends:1001 user:friends:1002
即可获取两人的共同好友 ID。去重统计:利用集合元素唯一性,统计 UV(独立访客)、去重 ID 列表等。
示例:统计某活动参与用户 ID(去重):SADD activity:100:users 5001 5002 5001
(自动去重后仅保留 5001、5002),SCARD activity:100:users
→ 2(参与人数)。
2.5 Sorted Set(ZSet,有序集合)
数据结构特点
- 有序唯一元素:每个元素关联一个
score
(浮点数),按score
升序排列,元素不可重复; - 高效排序与查询:支持按
score
范围、排名范围查询,底层基于 “跳跃表” 实现,时间复杂度 O (logN); - 支持
score
动态更新:修改元素score
后自动调整排序位置。
常用命令
命令 | 功能描述 | 示例 |
---|---|---|
ZADD key score member | 添加元素及 score | ZADD game:rank 95 "userA" 88 "userB" 92 "userC" (游戏排行榜) |
ZRANGE key start stop [WITHSCORES] | 按 score 升序获取排名范围内元素(0 为第一名) | ZRANGE game:rank 0 2 WITHSCORES → ["userB",88,"userC",92,"userA",95] |
ZREVRANGE key start stop [WITHSCORES] | 按 score 降序获取排名范围内元素 | ZREVRANGE game:rank 0 2 WITHSCORES → ["userA",95,"userC",92,"userB",88](前三名) |
ZSCORE key member | 获取元素 score | ZSCORE game:rank "userA" → 95 |
ZINCRBY key increment member | 元素 score 自增 increment(原子操作) | ZINCRBY game:rank 5 "userB" (userB 分数 + 5 → 93) |
ZRANK key member | 获取元素升序排名(0 为第一名) | ZRANK game:rank "userB" → 0(score 88 时排名第一) |
应用场景
排行榜系统:实时展示用户积分、商品销量、视频播放量等排名。
示例:直播平台礼物榜:观众送礼物时,ZINCRBY gift:rank:room100 50 "user888"
(用户 888 积分 + 50),前端调用ZREVRANGE gift:rank:room100 0 9 WITHSCORES
展示 “房间贡献榜 TOP10”。带权重的消息队列:按
score
(权重)优先级消费消息,高权重消息优先处理。
示例:订单处理系统:普通订单ZADD order:queue 1 "order1001"
,VIP 订单ZADD order:queue 10 "order1002"
,消费者通过ZREVRANGE order:queue 0 0
优先获取 VIP 订单处理。范围统计:按
score
范围筛选元素,如 “积分> 1000 的用户”“考试分数 80-90 分的学生”。
示例:教育平台统计成绩:ZRANGEBYSCORE exam:math 80 90
→ 获取数学成绩 80-90 分的学生 ID。
3. 高级数据类型
3.1 Bitmap(位图)
数据结构特点
- 位级操作:将字符串视为 “位数组”,每个位(bit)表示一个二值状态(0/1);
- 空间高效:1 字节 = 8 位,存储 100 万个状态仅需约 125KB;
- 支持位运算:与(AND)、或(OR)、异或(XOR)、非(NOT)及位计数。
常用命令
命令 | 功能描述 | 示例 |
---|---|---|
SETBIT key offset value | 设置指定偏移量的位值(0/1) | SETBIT sign:user:1001 5 1 (用户 1001 第 5 天签到,偏移量从 0 开始) |
GETBIT key offset | 获取指定偏移量的位值 | GETBIT sign:user:1001 5 → 1(第 5 天已签到) |
BITCOUNT key [start end] | 统计位值为 1 的数量(可指定字节范围) | BITCOUNT sign:user:1001 → 15(本月累计签到 15 天) |
BITOP op destkey key1 [key2] | 对多个 Bitmap 执行位运算,结果存到 destkey | BITOP AND sign:both:1001_1002 sign:user:1001 sign:user:1002 (两人共同签到的天数) |
应用场景
签到统计:记录用户每日签到状态,统计每月签到天数、连续签到天数。
示例:用户 1001 的 2025 年 8 月签到:SETBIT sign:user:1001:202508 0 1
(8 月 1 日签到,偏移量 0)、SETBIT ... 2 1
(8 月 3 日签到),BITCOUNT sign:user:1001:202508
→ 2(当月签到 2 天)。用户在线状态:用位偏移量表示用户 ID,位值 1 表示在线,0 表示离线,高效统计在线人数。
示例:平台在线用户:用户 5001 在线 →SETBIT online:20250814 5001 1
,BITCOUNT online:20250814
→ 1000(当前在线人数)。
3.2 HyperLogLog(基数统计)
数据结构特点
- 基数估算:用于统计 “集合中不重复元素的个数”(基数),而非存储元素本身;
- 空间极致优化:无论基数多大,单个 HyperLogLog 键仅占用约 12KB 内存;
- 概率性算法:存在 0.81% 的误差(可接受范围内),适合海量数据基数统计。
常用命令
命令 | 功能描述 | 示例 |
---|---|---|
PFADD key element1 [element2] | 添加元素到 HyperLogLog | PFADD uv:20250814 1001 1002 1001 1003 (网站当日 UV 统计) |
PFCOUNT key | 估算基数(不重复元素个数) | PFCOUNT uv:20250814 → 3(去重后 1001、1002、1003) |
PFMERGE destkey key1 [key2] | 合并多个 HyperLogLog 到 destkey | PFMERGE uv:202508 uv:20250801 uv:20250802 ... (合并 8 月每日 UV,统计月 UV) |
应用场景
UV 统计:统计网站 / 页面的独立访客数,无需存储用户 ID,节省内存。
示例:电商首页 UV:用户访问时PFADD uv:home:20250814 <user_id>
,当日结束后PFCOUNT uv:home:20250814
→ 50000(首页当日 UV)。搜索关键词去重:统计用户搜索过的不重复关键词数量,优化搜索推荐。
示例:PFADD search:keywords "phone" "book" "phone" "shoes"
,PFCOUNT search:keywords
→ 3(不重复关键词数)。
3.3 Geospatial(地理空间)
数据结构特点
- 地理位置存储:存储经纬度坐标(经度范围 - 180~180,纬度范围 - 85.05112878~85.05112878),支持距离计算、范围查询;
- 底层基于 ZSet 实现:将经纬度编码为
score
(Geohash 算法),通过 ZSet 命令间接操作。
常用命令
命令 | 功能描述 | 示例 |
---|---|---|
GEOADD key longitude latitude member | 添加地理位置 | GEOADD shops 116.403 39.914 "storeA" 116.410 39.905 "storeB" (北京商店坐标) |
GEODIST key member1 member2 [unit] | 计算两个位置的距离(unit:m/km/mi/ft) | GEODIST shops storeA storeB km → 1.2(storeA 与 storeB 相距 1.2 公里) |
GEORADIUS key longitude latitude radius unit [WITHDIST] | 搜索指定坐标半径内的位置 | GEORADIUS shops 116.4 39.9 5 km WITHDIST → 获取天安门 5 公里内的商店及距离 |
应用场景
附近的人 / 地点:基于用户当前坐标,搜索周边一定范围内的人或商家。
示例:外卖平台 “附近商家”:用户坐标(116.4, 39.9),GEORADIUS shops 116.4 39.9 3 km WITHDIST
→ 返回 3 公里内的商家及距离,按距离排序展示。地理位置围栏:监控设备是否进入 / 离开指定区域(如物流包裹是否到达配送范围)。
示例:物流场景中,GEORADIUS warehouse:1 120.0 30.0 10 km
→ 检查 10 公里内是否有包裹(通过包裹 ID 关联的坐标),触发配送通知。
3.4 Stream(消息流)
数据结构特点
- 持久化消息队列:支持消息持久化、消费确认、消费组(Consumer Group),解决 List 作为队列时的消息丢失、重复消费问题;
- 消息 ID 自增:每条消息有唯一 ID(格式
timestamp-sequence
,如1723600000000-0
),保证有序性; - 消费组机制:支持多消费者协同消费,每个消费者处理不同消息,避免重复消费。
常用命令
命令 | 功能描述 | 示例 |
---|---|---|
XADD key * field value [field value] | 添加消息(* 表示自动生成 ID) | XADD orderStream * type "pay" orderId "123" amount 99 (订单支付消息) |
XREAD COUNT count STREAMS key startId | 读取消息(startId=0-0 表示从头读) | XREAD COUNT 1 STREAMS orderStream 0-0 → 读取 1 条消息 |
XGROUP CREATE key groupName startId | 创建消费组 | XGROUP CREATE orderStream group1 $ ($ 表示从最新消息开始消费) |
XREADGROUP GROUP groupName consumer COUNT count STREAMS key > | 消费组内消费者读取消息(> 表示未消费消息) | XREADGROUP GROUP group1 consumer1 COUNT 1 STREAMS orderStream > → consumer1 读取 1 条未消费消息 |
XACK key groupName msgId | 确认消息已消费 | XACK orderStream group1 1723600000000-0 (确认消息处理完成) |
应用场景
- 可靠消息队列:替代 List 实现高可靠消息传递,支持消息重试、消费确认,适合订单处理、日志收集等场景。
示例:电商订单系统:订单支付后XADD payStream * orderId "123" status "success"
,下游物流服务通过消费组XREADGROUP
读取消息并处理,处理完成后XACK
确认,避免消息丢失。
4. 持久化机制(RDB vs AOF)
Redis 支持两种持久化方式,可单独启用或混合使用,核心目标是防止内存数据丢失。
4.1 RDB(Redis Database)
- 原理:在指定时间间隔内生成内存数据的快照文件(.rdb),保存到磁盘。
- 触发方式:
- 手动触发:
SAVE
(阻塞 Redis,适合离线备份)、BGSAVE
(fork 子进程执行,不阻塞主进程); - 自动触发:配置文件中
save <seconds> <changes>
(如save 3600 1
:3600 秒内有 1 次修改则触发)。
- 手动触发:
- 优缺点:
- 优点:文件体积小,恢复速度快(直接加载快照到内存);
- 缺点:数据安全性低,两次快照间的修改可能丢失(如 5 分钟快照间隔,崩溃时丢失 5 分钟数据)。
4.2 AOF(Append Only File)
- 原理:记录所有写命令(如 SET、HSET)到日志文件(.aof),重启时通过重新执行命令恢复数据。
- 配置参数:
appendfsync always
:每条命令立即刷盘(安全性最高,性能最差);appendfsync everysec
:每秒刷盘一次(默认,平衡安全与性能,最多丢失 1 秒数据);appendfsync no
:由操作系统决定刷盘时机(性能最好,安全性最低)。
- AOF 重写:解决 AOF 文件膨胀问题,通过
BGREWRITEAOF
命令生成新 AOF 文件(合并重复命令,如多次 INCR 合并为最终值)。 - 优缺点:
- 优点:数据安全性高,丢失数据少;
- 缺点:文件体积大,恢复速度慢(需重新执行所有命令)。
4.3 混合持久化(Redis 4.0+)
- 原理:AOF 文件头部存储 RDB 快照,尾部存储增量 AOF 命令,结合 RDB 恢复快和 AOF 数据全的优点。
- 配置:
aof-use-rdb-preamble yes
(默认开启)。
4.4 持久化方案选择
场景 | 推荐方案 | 理由 |
---|---|---|
数据安全性要求高(如金融交易) | AOF(everysec)+ 混合持久化 | 最多丢失 1 秒数据,恢复速度较纯 AOF 快 |
性能优先(如缓存场景) | RDB(或禁用持久化) | 减少磁盘 IO 开销,缓存数据可从数据库重建 |
灾备需求 | RDB 定期备份 + AOF 实时记录 | RDB 用于全量恢复,AOF 用于增量数据补充 |
5. 高可用方案(主从复制与哨兵)
5.1 主从复制
- 作用:实现数据备份与读写分离,主库(Master)负责写操作,从库(Slave)同步主库数据并提供读服务。
- 原理:
- 全量复制:从库首次连接主库时,主库生成 RDB 快照发送给从库,从库加载快照后,主库再发送缓冲的增量命令;
- 增量复制:后续主库将写命令记录到
repl_backlog_buffer
,从库通过偏移量(offset)同步增量命令。
- 配置:从库配置
slaveof <master-ip> <master-port>
,主库无需额外配置。 - 应用:读写分离(主库写、从库读),如电商商品详情查询走从库,减轻主库压力。
5.2 哨兵(Sentinel)
- 作用:监控主从节点状态,实现自动故障转移(主库挂了后从库自动升级为主库)。
- 核心功能:
- 监控:定期检查主从节点是否存活;
- 通知:通过 API 向管理员或其他应用发送故障通知;
- 自动故障转移:主库故障时,选举从库升级为主库,其他从库重新指向新主库。
- 部署:通常部署 3 个哨兵节点(奇数,避免脑裂),配置文件指定监控的主库信息。
- 示例:一主二从 + 三哨兵架构,主库挂了后,哨兵通过投票选举其中一个从库升级为主库,客户端通过哨兵获取新主库地址继续访问。
6. Redis Cluster(分布式集群)
6.1 核心概念
- 分片存储:将数据按 “槽位”(共 16384 个槽)分配到多个节点,每个节点负责一部分槽位(如 3 主节点各负责 5461 个槽);
- 主从复制:每个主节点可配置从节点,实现数据备份与故障转移;
- 去中心化:无中心节点,客户端直接与槽位对应节点通信,节点间通过 Gossip 协议交换状态。
6.2 数据路由
- 槽位计算:客户端根据
CRC16(key) % 16384
计算 key 所属槽位; - 重定向:若客户端连接的节点不负责目标槽位,返回
MOVED
错误,指引客户端连接正确节点。
6.3 应用场景
- 海量数据存储:突破单机内存限制,如存储 100GB 数据,可通过 3 主 3 从集群(每主节点 30GB + 内存)分片存储;
- 高并发访问:多节点分担读写压力,提升整体吞吐量。
7. 性能优化实践
7.1 键设计规范
- 命名规范:用冒号分隔命名空间,如
user:1001:info
、order:202508:list
,避免键名混乱; - 避免大键:单个 key 存储数据不超过 10KB(如大 List、大 Hash 拆分存储),防止操作阻塞 Redis;
- 过期键清理:对缓存数据设置合理过期时间(
EX
/PX
),避免内存溢出。
7.2 内存淘汰策略
当内存达到maxmemory
限制时,Redis 通过以下策略淘汰键(配置maxmemory-policy
)
策略名称 | 说明(优先级从高到低) |
---|---|
volatile-lru | 从设置过期时间的键中,淘汰最近最少使用的键 |
allkeys-lru | 从所有键中,淘汰最近最少使用的键 |
volatile-ttl | 从设置过期时间的键中,淘汰剩余时间最短的键 |
noeviction | 不淘汰键,写操作返回错误(默认,不推荐生产环境) |
推荐配置:缓存场景用allkeys-lru
(优先淘汰冷数据),业务数据用volatile-lru
(仅淘汰过期缓存)。
7.3 命令与网络优化
- 批量操作:用
MSET
/MGET
(String)、HMSET
/HMGET
(Hash)替代循环单条命令,减少网络往返; - Pipeline:客户端将多个命令打包发送,Redis 批量执行后返回结果,适合大量小命令场景(如批量更新库存);
- 避免阻塞命令:禁用
KEYS *
(遍历所有键,阻塞 Redis),改用SCAN
(迭代遍历);避免HGETALL
(大 Hash 全量获取),改用HMGET
指定字段。
8. 总结
Redis 凭借丰富的数据结构、高性能、持久化、分布式支持等特性,成为缓存、数据库、消息队列等场景的首选工具。核心知识点包括:
- 数据类型:String(缓存、计数器)、Hash(对象存储)、List(队列)、Set(集合运算)、ZSet(排行榜)等,需结合场景选择合适类型;
- 持久化:RDB(性能优先)与 AOF(安全优先),生产环境推荐混合持久化;
- 高可用:主从复制(读写分离)+ 哨兵(故障转移);
- 分布式:Redis Cluster 实现分片存储,突破单机限制。
通过合理设计键结构、优化命令、配置持久化与高可用策略,可充分发挥 Redis 性能,支撑高并发业务场景。