很多人刚学 Redis 时,容易只背 SETGETHSETLPUSH 这些命令。

但真正写项目时,更重要的问题其实是:

什么场景应该选什么 Redis 数据结构?

比如:

  • 做缓存,用 String 还是 Hash
  • 做排行榜,为什么一般用 ZSet
  • 做去重、标签、共同好友,为什么适合用 Set
  • 做最新动态,为什么可以用 List
  • 做分布式锁,为什么不建议只写 SETNX

本文从 Redis 的核心特点、常用数据类型、典型应用场景和命令示例几个角度,对 Redis 基础做一次系统整理。

一、Redis 是什么

Redis 是一个高性能的内存数据结构服务,常见用途包括:

  • 缓存
  • 计数器
  • 分布式锁
  • 排行榜
  • 消息队列
  • Session 共享
  • 标签系统
  • 限流
  • 延迟队列

Redis 不只是简单的 Key-Value 数据库,它还支持多种数据结构,比如:

  • String
  • Hash
  • List
  • Set
  • Sorted Set,也叫 ZSet

理解 Redis 的关键,不是把所有命令都背下来,而是先知道每种数据结构适合解决什么问题。

二、Redis 的核心特点

1. 基于内存,读写速度快

Redis 的大部分读写操作都在内存中完成,所以性能很高。

这也是 Redis 经常被用作缓存的原因。比如商品详情、用户信息、热点数据,如果每次都查 MySQL,数据库压力会很大;把高频数据放到 Redis 中,可以显著减少数据库访问次数。

2. 支持丰富的数据结构

Redis 不只是普通的字符串 KV 存储,它还支持多种数据结构。

常见数据结构如下:

数据结构 常见用途
String 缓存、计数器、分布式锁
Hash 对象存储、购物车
List 最新动态、简易消息队列
Set 去重、标签、共同好友
ZSet 排行榜、延迟队列、滑动窗口限流

不同数据结构适合不同业务场景,选对结构比单纯会写命令更重要。

3. 支持持久化

Redis 数据主要在内存中,但它也支持持久化机制,可以把数据保存到磁盘中。

常见持久化方式有:

  • RDB:按时间点生成快照。
  • AOF:记录写命令日志。

这样可以降低服务重启或机器故障时的数据丢失风险。

4. 核心网络模型是单线程

Redis 的核心网络请求处理模型是单线程的,它通过 I/O 多路复用处理大量连接。

单线程模型减少了多线程上下文切换带来的开销,也避免了很多并发竞争问题。

注意:这里说的单线程主要指 Redis 核心命令执行和网络请求处理模型,并不代表 Redis 内部所有任务都只有一个线程。

5. 支持高可用和分布式

Redis 支持:

  • 主从复制
  • Sentinel 哨兵
  • Cluster 集群

在实际项目中,可以通过这些机制提升 Redis 的可用性和扩展能力。

三、Redis 常见应用场景

Redis 最常见的场景包括:

场景 说明
缓存 减轻 MySQL 等数据库压力
排行榜 使用 ZSet 实现实时排名
计数器 使用 INCR 统计阅读量、点赞数
限流 限制接口访问频率
分布式锁 防止并发操作同一资源
Session 共享 多服务共享用户登录状态
购物车 使用 Hash 存商品和数量
标签系统 使用 Set 存储不重复标签
共同好友 使用 Set 交集计算
延迟队列 使用 ZSet 按时间戳排序

下面按数据类型分别整理。

四、通用 Key 操作

通用 Key 命令对所有数据类型都有效,主要用于管理 key。

# 判断 key 是否存在
EXISTS user:1001

# 查看 key 的数据类型
TYPE user:1001

# 删除 key
DEL user:1001

# 异步删除 key,适合删除大 key
UNLINK user:1001

# 设置过期时间,单位是秒
EXPIRE user:1001 3600

# 查看剩余过期时间
TTL user:1001

# 移除过期时间
PERSIST user:1001

KEYS * 为什么生产环境慎用

很多初学者会用:

KEYS *

这个命令可以查所有 key,但生产环境要慎用。

原因是 KEYS * 会遍历 Redis 中所有 key,如果数据量很大,可能阻塞 Redis,影响线上请求。

生产环境更推荐使用 SCAN 分批扫描:

SCAN 0 MATCH user:* COUNT 100

五、String:缓存、计数器、分布式锁

String 是 Redis 最基础的数据类型。

它可以存:

  • 普通字符串
  • 数字
  • JSON 字符串
  • 二进制数据

1. 基础命令

# 设置值
SET name "Alice"

# 获取值
GET name

# 批量设置
MSET k1 "v1" k2 "v2"

# 批量获取
MGET k1 k2

# 设置值并指定过期时间
SETEX user:1001 3600 "{name:Tom,age:24}"

2. 应用场景:缓存

比如缓存商品详情:

SETEX product:1001 3600 "{id:1001,title:手机,price:3999}"
GET product:1001

含义:

  • product:1001 是缓存 key。
  • 3600 表示缓存 1 小时。
  • 后续请求可以先查 Redis,Redis 没有再查数据库。

3. 应用场景:计数器

Redis 的 INCRDECRINCRBY 都是原子操作,适合做高并发计数。

# 阅读量 +1
INCR article:1001:views

# 点赞数 +1
INCR article:1001:likes

# 分数增加 10
INCRBY user:1001:score 10

典型场景:

  • 文章阅读量
  • 视频播放量
  • 点赞数
  • 库存扣减

4. 应用场景:分布式锁

很多资料会写:

SETNX lock:order:1001 uuid
EXPIRE lock:order:1001 30

但更推荐使用一条命令完成加锁和过期时间设置:

SET lock:order:1001 uuid NX EX 30

含义:

  • NX:key 不存在时才设置成功。
  • EX 30:30 秒后自动过期。
  • uuid:锁的唯一标识,释放锁时要校验是不是自己的锁。

为什么不建议简单使用 SETNX + EXPIRE

因为这两条命令不是一个原子操作。如果 SETNX 成功后程序崩溃,EXPIRE 没执行,就可能出现锁永远不过期的问题。

六、Hash:对象存储、购物车

Hash 适合存储对象结构。

可以理解为:

一个 Redis key 里面,又存了一组 field-value

1. 基础命令

# 设置用户对象
HSET user:1001 name "Tom" age 24 city "Beijing"

# 获取某个字段
HGET user:1001 name

# 获取多个字段
HMGET user:1001 name age

# 获取全部字段和值
HGETALL user:1001

# 判断字段是否存在
HEXISTS user:1001 city

# 删除字段
HDEL user:1001 age

# 对数字字段自增
HINCRBY user:1001 age 1

2. 应用场景:用户信息

如果用 String 存 JSON:

SET user:1001 "{name:Tom,age:24,balance:100}"

要修改 balance 时,通常需要:

取出整个 JSON -> 反序列化 -> 修改字段 -> 序列化 -> 写回 Redis

如果用 Hash,可以直接修改字段:

HINCRBY user:1001 balance 100

这样可以减少网络传输和序列化成本。

3. 应用场景:购物车

购物车可以这样设计:

key = cart:user_1
field = 商品 ID
value = 商品数量

示例:

# 添加商品
HSET cart:user_1 product_88 1

# 商品数量 +1
HINCRBY cart:user_1 product_88 1

# 删除商品
HDEL cart:user_1 product_88

# 获取购物车所有商品
HGETALL cart:user_1

Hash 很适合这种“一个对象下面有多个字段”的场景。

七、List:最新动态、简易消息队列

List 是一个双向链表。

特点:

  • 按插入顺序保存。
  • 元素可以重复。
  • 两端插入和弹出速度很快,通常是 O(1)

1. 基础命令

# 从左侧插入
LPUSH mylist "A" "B"

# 从右侧插入
RPUSH mylist "C"

# 从左侧弹出
LPOP mylist

# 从右侧弹出
RPOP mylist

# 获取指定范围
LRANGE mylist 0 -1

# 获取长度
LLEN mylist

# 阻塞式弹出
BRPOP mylist 10

2. 应用场景:最新动态

比如做一个用户动态列表:

# 发布新动态
LPUSH feed:user_1 post_1001
LPUSH feed:user_1 post_1002

# 查询最新 10 条动态
LRANGE feed:user_1 0 9

因为 LPUSH 是从左侧插入,所以最新内容总是在前面。

适合场景:

  • 最新评论
  • 用户动态
  • 新闻列表
  • 简单 Feed 流

3. 应用场景:简易消息队列

生产者写入任务:

LPUSH mq:email task_1001

消费者阻塞等待任务:

BRPOP mq:email 10

这种方式可以做简单异步任务,比如:

  • 发短信
  • 发邮件
  • 生成报表

注意:如果是复杂消息队列需求,比如确认机制、重试、死信队列、消费组,建议使用 Redis Stream、RabbitMQ 或 Kafka。

八、Set:去重、标签、共同好友

Set 是无序且不重复的集合。

特点:

  • 自动去重。
  • 支持交集、并集、差集。
  • 适合关系计算。

1. 基础命令

# 添加元素
SADD tags "redis" "mysql" "java"

# 查看所有元素
SMEMBERS tags

# 删除元素
SREM tags "java"

# 判断元素是否存在
SISMEMBER tags "mysql"

# 获取元素数量
SCARD tags

2. 应用场景:数据去重

比如统计每天独立访客 UV:

SADD uv:2026-05-27 ip_1
SADD uv:2026-05-27 ip_2
SADD uv:2026-05-27 ip_1
SCARD uv:2026-05-27

因为 Set 会自动去重,所以同一个 IP 多次访问也只算一次。

3. 应用场景:标签系统

比如给用户打标签:

SADD user:1001:tags "科技" "数码" "游戏"
SMEMBERS user:1001:tags

如果要判断用户是否有某个标签:

SISMEMBER user:1001:tags "科技"

4. 应用场景:共同好友

假设:

SADD friends:user_1 user_2 user_3 user_4
SADD friends:user_2 user_3 user_4 user_5

求共同好友:

SINTER friends:user_1 friends:user_2

求 user_2 有、user_1 没有的好友:

SDIFF friends:user_2 friends:user_1

这类交集、差集操作是 Set 的强项。

5. 应用场景:随机抽奖

# 添加参与人
SADD lottery:pool user_1 user_2 user_3 user_4

# 随机查看 2 个,不删除
SRANDMEMBER lottery:pool 2

# 随机抽出 2 个,并从集合中删除
SPOP lottery:pool 2

如果要求不能重复中奖,就用 SPOP

九、ZSet:排行榜、延迟队列、滑动窗口限流

ZSet,也叫 Sorted Set,有序集合。

它和 Set 一样,元素不重复;但 ZSet 会给每个元素关联一个 score,Redis 会根据 score 排序。

1. 基础命令

# 添加成员和分数
ZADD game_rank 100 "PlayerA" 200 "PlayerB" 150 "PlayerC"

# 按分数从低到高查询
ZRANGE game_rank 0 -1 WITHSCORES

# 按分数从高到低查询
ZREVRANGE game_rank 0 -1 WITHSCORES

# 查看某个成员分数
ZSCORE game_rank "PlayerA"

# 增加分数
ZINCRBY game_rank 50 "PlayerA"

# 查看排名,从高到低
ZREVRANK game_rank "PlayerA"

# 按分数区间查询
ZRANGEBYSCORE game_rank 100 200

2. 应用场景:排行榜

比如游戏排行榜:

ZADD game_rank 100 "PlayerA"
ZADD game_rank 200 "PlayerB"
ZINCRBY game_rank 50 "PlayerA"
ZREVRANGE game_rank 0 9 WITHSCORES

含义:

  • ZADD:添加玩家和分数。
  • ZINCRBY:增加玩家分数。
  • ZREVRANGE 0 9:查询前 10 名。

ZSet 非常适合做:

  • 游戏积分榜
  • 热搜榜
  • 音乐播放榜
  • 直播打赏榜

3. 应用场景:延迟队列

比如订单 30 分钟未支付自动取消。

设计方式:

member = 订单 ID
score = 未来需要执行的时间戳

示例:

ZADD delay:orders 1779863000 order_1001
ZRANGEBYSCORE delay:orders -inf 1779863000
ZREM delay:orders order_1001

后台程序定时扫描:

取出 score <= 当前时间戳的订单 -> 执行取消逻辑 -> 从 ZSet 删除

4. 应用场景:滑动窗口限流

比如限制某个用户 60 秒内最多访问 100 次。

可以把每一次请求都存入 ZSet:

member = 请求唯一 ID
score = 当前时间戳

示例:

# 记录本次请求
ZADD rate:user_1 1779863000 req_001

# 删除 60 秒之前的请求
ZREMRANGEBYSCORE rate:user_1 -inf 1779862940

# 统计最近 60 秒请求数
ZCOUNT rate:user_1 1779862940 1779863000

# 设置过期时间,避免长期占用内存
EXPIRE rate:user_1 120

如果统计结果超过 100,就拒绝请求。

ZSet 适合这种“按时间戳排序并按范围查询”的场景。

十、Redis 数据类型选型表

实际开发中可以按下面这张表快速判断。

场景 推荐数据结构 常用命令
普通缓存 String SETEX / GET
JSON 对象缓存 String SETEX / GET
字段级对象存储 Hash HSET / HGET / HINCRBY
阅读量、点赞数 String INCR / INCRBY
分布式锁 String SET key value NX EX seconds
购物车 Hash HSET / HINCRBY / HGETALL
最新动态 List LPUSH / LRANGE
简易消息队列 List LPUSH / BRPOP
标签系统 Set SADD / SISMEMBER
去重统计 Set SADD / SCARD
共同好友 Set SINTER / SDIFF
排行榜 ZSet ZADD / ZINCRBY / ZREVRANGE
延迟队列 ZSet ZADD / ZRANGEBYSCORE
滑动窗口限流 ZSet ZADD / ZREMRANGEBYSCORE / ZCOUNT

可以简单记成:

缓存、计数、锁 -> String
对象字段 -> Hash
有顺序的新旧数据 -> List
去重、标签、交并差 -> Set
排序、排行、时间范围 -> ZSet

十一、常见注意点

1. 生产环境慎用 KEYS *

KEYS * 会遍历所有 key,数据量大时可能阻塞 Redis。

推荐使用:

SCAN 0 MATCH user:* COUNT 100

2. 删除大 key 优先考虑 UNLINK

DEL 删除大 key 时可能阻塞 Redis。

Redis 4.0 以后可以使用:

UNLINK big:key

它会异步释放内存,更适合删除大对象。

3. 分布式锁不要只写 SETNX

不推荐:

SETNX lock:order:1001 uuid
EXPIRE lock:order:1001 30

推荐:

SET lock:order:1001 uuid NX EX 30

加锁和设置过期时间放在一条命令里,原子性更好。

4. List 可以做简易队列,但不是专业 MQ

LPUSH + BRPOP 可以实现简单队列,但如果业务需要:

  • 消息确认
  • 重试机制
  • 死信队列
  • 消费组
  • 消息持久化语义

就应该考虑 Redis Stream、RabbitMQ 或 Kafka。

5. String 存 JSON 和 Hash 存对象要按场景选择

如果每次都是整体读写对象,用 String 存 JSON 很简单。

如果经常修改对象中的某个字段,用 Hash 更合适。

例如只修改用户余额:

HINCRBY user:1001 balance 100

比每次取出整个 JSON 再写回更清晰。

十二、总结

Redis 基础学习不要只停留在命令层面,更应该理解数据结构和业务场景之间的关系。

可以按下面这个思路记:

  • String:最基础,适合缓存、计数器、分布式锁。
  • Hash:适合对象字段存储,比如用户信息、购物车。
  • List:适合按顺序保存数据,比如最新动态、简易队列。
  • Set:适合去重和集合运算,比如标签、共同好友、抽奖。
  • ZSet:适合排序和按范围查询,比如排行榜、延迟队列、限流。

最后再记一句:

Redis 的重点不是会多少命令,而是能不能根据业务场景选对数据结构。

参考资料

更多推荐