Redis 使用手册
Redis 是基于内存的键值存储,常被当作缓存、消息队列、分布式锁甚至轻量数据库使用。它之所以通用,关键在于其丰富的数据结构——选对结构,命令和场景往往自然成立。本文是 Redis 使用侧手册,系统梳理各数据结构的核心命令与适用场景;底层编码、Lua 执行模型、过期淘汰、持久化与集群架构等原理见姊妹篇《Redis 底层原理与架构》。
全局视角
Redis 的 key 是二进制安全的字符串,value 则可以是以下几种类型之一。选型时先看用途,再结合访问模式决定结构。
| 类型 | 典型用途 |
|---|---|
| String | 缓存、计数器、分布式锁 |
| List | 消息队列、最新列表 |
| Hash | 对象存储 |
| Set | 去重、标签、抽奖 |
| ZSet | 排行榜、延迟队列 |
| Stream | 消息流、事件日志 |
| Bitmap | 签到、活跃统计 |
| HyperLogLog | 基数统计 |
| Geo | 附近的人/店 |
| Lua 脚本 | 原子复合操作、分布式锁释放 |
String
String 是最基础也最被低估的类型。它不仅能存文本,还能存数字、二进制(图片、序列化对象),最大 512MB。
核心命令
1 | |
SET 的 NX 选项是构建分布式锁的基础。
适用场景
- 缓存对象:序列化后的 JSON、Protobuf。建议加 TTL 防止冷数据堆积。
- 计数器:阅读量、点赞数。
INCR原子且高效,单实例可达十万级 QPS。 - 分布式锁:
SET lock:order:123 token NX EX 30,释放时用 Lua 校验 token 再DEL,避免误删他人锁。 - 限流器:固定窗口用
INCR+EXPIRE;滑动窗口可结合 ZSet。
分布式锁建议优先用 Redlock 或 Redisson 封装,单节点
SET NX在主从故障切换时存在锁丢失风险。
List
双向链表语义的有序字符串集合,支持两端高效 push/pop。
核心命令
1 | |
适用场景
- 消息队列:
LPUSH+BRPOP实现「生产者-消费者」。BRPOP阻塞避免空轮询。 - 最新 N 条列表:朋友圈最新动态、最新登录记录,配合
LPUSH+LTRIM固定长度。 - 栈/队列:同端操作即栈(
LPUSH+LPOP),异端操作即队列。 - 安全队列:
RPOPLPUSH(7.0 后LMOVE)把处理中的消息搬到备份 list,处理失败可回滚。
List 做消息队列缺乏 ack 机制与消费组,复杂场景请用 Stream。
Hash
字段-值映射,值本身也是字符串。一个 Hash 就像一个对象。
核心命令
1 | |
适用场景
- 对象存储:用户、商品属性。相比把整个 JSON 存 String,Hash 可以局部更新而不必读写整体,省带宽。
- 计数器集合:一篇文章的点赞、转发、评论各占一个字段,
HINCRBY article:1 likes 1。 - 购物车:
cart:uid中 field=商品ID,value=数量。
当字段较少且整体读取频繁时,String + JSON 也合理;当需要字段级独立读写时,Hash 更优。
Set
无序、唯一字符串集合,支持丰富的集合运算。
核心命令
1 | |
适用场景
- 去重:UV 中转存储、敏感词集合。
SISMEMBERO(1) 判断。 - 标签/共同关注:用户标签用 Set,
SINTER求共同好友、共同标签。 - 抽奖:
SRANDMEMBER抽取(可重复)或SPOP抽取(不可重复)。 - 黑白名单:IP 黑名单、令牌白名单。
大集合做
SINTER较重,可让小集合在前,或用SINTERSTORE预计算。
ZSet(Sorted Set)
有序集合,每个元素关联一个 score,按 score 排序且元素唯一。Redis 中表达力最强的结构。
核心命令
1 | |
ZADD 支持 NX(仅新增)、XX(仅更新)、GT/LT(仅当更大/更小时更新)、CH(返回变更数)等修饰符。
适用场景
- 排行榜:游戏积分榜、热搜榜。
ZADD更新分数,ZREVRANGE 0 9取 Top10,ZREVRANK查个人排名。 - 延迟队列:score 存「到期时间戳」,消费者定时
ZRANGEBYSCORE 0 now取出到期任务并ZREM。务必用 Lua 保证「取出+删除」原子,避免多消费者重复消费。 - 时间线/范围查询:以时间戳为 score,按时间窗口检索消息、日志。
- 带权重的并集:
ZUNIONSTORE做多维度综合评分。
Stream
5.0 引入,专为消息流设计,可看作「自带持久化、消费组、ack 的日志结构」。是 List 在消息队列场景的正式替代。
Stream 是只追加的日志,每条消息有自动生成的 ID(<毫秒时间戳>-<序号>),可按 ID 或时间范围查询。支持消费组:组内消费者负载均衡,每条消息仅被组内一个消费者处理,ack 后才从待确认列表移除。
核心命令
1 | |
适用场景
- 可靠消息队列:事件驱动架构、领域事件广播。消费组 + ack + XCLAIM 提供了 at-least-once 语义。
- 事件溯源 / 审计日志:天然只追加、可回放、按时间范围查询。
- 指标流:采集指标后供多消费者各自消费。
Stream 持久化依赖 Redis 的 RDB/AOF,并非独立的 MQ。对消息可靠性要求极高(金融级)仍建议用 Kafka/RabbitMQ;轻量、与缓存同栈的场景 Stream 很合适。
Bitmap
并非独立类型,而是 String 上的位操作。一个 String 最多 512MB,可容纳约 42 亿个 bit。

核心命令
1 | |
适用场景
- 签到 / 打卡:以
uid或「月份+uid」为 key,1 bit 表示一天,省内存。 - 活跃用户统计:每天一个 Bitmap,bit 位对应 uid。
BITCOUNT算日活,BITOP AND/OR算留存、连续活跃。 - 布隆过滤器:用多个 hash +
SETBIT实现近似去重,用于缓存穿透防护、海量 URL 判重。
Bitmap 适合「用户 id 连续且稠密」的场景,否则空间浪费。稀疏场景可考虑 RoaringBitmap(需客户端实现)。
HyperLogLog
基数(去重后元素个数)的近似统计算法,标准误差约 0.81%,每个 key 固定占 12KB。

核心命令
1 | |
适用场景
- UV / 独立 IP 数:千万级去重统计,相比 Set 节省几个数量级内存。
- 日活 / 月活合并:
PFMERGE把每日 HLL 合并为月活。
误差在 1% 以内对统计类指标完全可接受。需要精确去重请用 Set 或 Bitmap;只关心「大概多少」用 HLL。
Geo
地理位置结构,基于 ZSet + GeoHash 将经纬度编码为 score,从而复用 ZSet 的范围能力。

核心命令
1 | |
GEOSEARCH(6.2+ 取代 GEORADIUS)支持按半径/矩形搜索、按距离排序、限制数量。
适用场景
- 附近的人 / 附近的店:外卖、打车、社交 LBS 查询。
- 距离计算:物流 ETA、配送范围判定。
- 围栏判断:判断点是否在某区域半径内。
Geo 底层是单个 ZSet,元素不宜过多(百万级以上范围查询会变慢),且只支持球面距离近似。
Lua 脚本
Redis 从 2.6 起内置 Lua 解释器,允许将多条命令打包为一段脚本原子执行——整个脚本期间 Redis 不会切换到其他客户端,天然避免了竞态条件。这是构建复杂原子操作的首选方案。
基本用法
1 | |
重要:所有 key 必须通过
KEYS传入,不能在脚本里硬编码或拼接。Redis Cluster 依赖此约定将脚本路由到正确的分片;单节点也建议遵守,以便未来迁移。
脚本缓存与 EVALSHA
每次 EVAL 都会发送完整脚本,网络开销大。Redis 会自动缓存脚本的 SHA1 摘要,后续可用 EVALSHA 只发摘要:
1 | |
客户端库(如 Redisson、lettuce)通常封装了「先 EVALSHA,NOSCRIPT 时回退 EVAL」的重试逻辑,开发者无感知。
SCRIPT 调试
Redis 7.0+ 引入了函数机制(FUNCTION LOAD/CALL),可命名、可持久化,是脚本的进化版;但 EVAL 仍然完全可用且更简单。调试脚本可用:
1 | |
经典模式
安全的分布式锁释放
1 | |
1 | |
滑动窗口限流
1 | |
延迟队列消费
1 | |
注意事项
- 保持脚本短小:长脚本阻塞所有客户端,影响可用性。经验法则:脚本执行时间 < 1ms,绝对不超过
lua-time-limit。 - KEYS 规范:始终通过
KEYS传入 key 名,不要在 ARGV 或字符串拼接中构造 key——这对集群模式是硬性要求。 - 避免全局变量:7.0+ 默认禁止,但仍需注意不要在脚本间意外共享状态(Lua 环境是单实例复用的)。
- 错误处理:生产脚本建议用
redis.pcall包裹可能失败的命令,检查返回值的err字段,而非让整个脚本崩溃。 - 替代方案:如果只需要简单原子性(如
SET NX EX),优先用原生命令而非 Lua;Lua 适合「读-判-写」这种需要中间逻辑的复合操作。
键过期(TTL)
为 key 设置过期时间,是 Redis 控制内存、回收冷数据的核心手段。
核心命令
1 | |
重新
SET同名 key 会清除其过期时间,需再次指定;EXPIRE只改到期时间,不触碰 value。
注意事项
- 避免大批量同时过期:同一刻大量 key 失效会引发「缓存雪崩」,请求穿透到 DB。可对 TTL 加随机抖动(
base + random(0, 300))打散。 - 从库过期:删除由主库主导,完成后传播
DEL给从库;3.2+ 从库读取过期 key 时也会返回不存在(惰性),但实际删除仍以主库为准,避免主从不一致。
键遍历(SCAN)
KEYS pattern 一次性遍历整个 keyspace 并阻塞主线程,百万级 key 可致服务级联超时,生产环境应视为禁用。SCAN 系列以游标为基础分批增量遍历,是生产遍历 key 的唯一推荐方式。
核心命令
1 | |
SCAN 返回 [next_cursor, elements],当 next_cursor 为 0 时遍历完成。
与 KEYS 对比
| 维度 | KEYS | SCAN |
|---|---|---|
| 阻塞 | 全程阻塞主线程 | 单次短阻塞 |
| 复杂度 | O(N) 一次性 | O(N) 分摊多次 |
| 一致性 | 弱一致 | 同样弱一致 |
| 生产可用 | 禁用 | 推荐 |
注意事项
- 生产禁用 KEYS:用 SCAN/HSCAN 等替代,后台清理任务尤其要注意。
- MATCH 为服务端过滤:在
COUNT抽样之后再筛,因此SCAN 0 MATCH user:* COUNT 100可能返回 0 个但 cursor 非零,必须继续。 - TYPE 过滤(6.0+):
SCAN ... TYPE string只返回指定类型,省去客户端二次判断。 - 集群模式:需对每个节点分别 SCAN,或用集群客户端的统一遍历接口。
- 大 key 探测:遍历中可结合
MEMORY USAGE定位大 key,但该命令本身有开销,避免高频调用。
集群(Cluster)
Redis Cluster 通过数据分片将 key 分散到多个主节点,提供横向扩容与自动故障转移。本节仅介绍使用侧命令与约束,架构与故障转移原理见姊妹篇《Redis 底层原理与架构》。
核心命令
1 | |
Hash Tag
slot 默认对 key 整体做 CRC16,因此任意两个不同 key 都可能落在不同槽。但有些操作要求多 key 必须在同一节点:MGET、SUNION、MULTI 事务、Lua 脚本等。为此 Redis 支持 Hash Tag——若 key 中包含 {...},则仅花括号内的子串参与哈希:
1 | |
三个 key 槽位相同,必落在同一节点,于是可安全执行 MGET、事务或 Lua 复合操作。原则:把逻辑上需要原子/聚合访问的一组 key 绑定同一 tag,无需关心前缀后缀差异。
注意避免数据倾斜:若 tag 取值集中(如都用
{shard0}),相关 key 会全压在一个节点,失去分片意义。tag 应选择高基数、访问均匀的业务维度(如用户 ID、订单 ID),让数据天然散开。
注意事项
- 容量规划:单节点内存上限即整体的单分片上限;某分片数据过多会触发淘汰或 OOM,需通过 Hash Tag 设计与 reshard 预平衡。
- 重定向成本:缓存槽映射后稳定期几乎零重定向,但大规模 reshard 期间
MOVED/ASK会上升,应避开业务高峰。 - 从节点读:默认从节点读会返回
MOVED指回主节点;执行READONLY后从节点可处理读请求,分散读压力。 - 何时上 Cluster:数据量未达单机瓶颈、高可用可用 Sentinel 解决时,单机 + 主从 + Sentinel 更简单;真正需要水平扩展写与存储时才上 Cluster。
通用机制与选型建议
事务与原子性
- MULTI/EXEC:命令打包顺序执行,不支持回滚(某条出错其余仍执行)。
- WATCH:乐观锁,监视 key 在 EXEC 前若被修改则整个事务失败。
- Lua 脚本:原子执行多条命令,是构建复杂原子操作(如安全分布式锁、延迟队列消费)的首选。详见上方 Lua 脚本 章节。
选型决策
- 需要单值缓存/计数/锁 → String
- 需要有序、两端操作、队列 → List
- 需要字段级读写对象 → Hash
- 需要去重/集合运算 → Set
- 需要按分数排序/排行榜/范围 → ZSet
- 需要可靠消费组队列 → Stream
- 需要海量布尔位/布隆 → Bitmap
- 需要海量基数近似统计 → HyperLogLog
- 需要地理位置 → Geo
- 需要原子复合操作(读-判-写) → Lua 脚本
一个常见反模式
用 String 存对象 JSON 并整体读写,在「字段多、只改其中一两个、写频繁」时,会浪费带宽并放大并发覆盖问题——此时应改用 Hash,按字段独立更新。反之,对象小且总是一起读写时,String + JSON 反而更简单高效。结构与访问模式匹配,才是 Redis 用的好的核心。


