Redis 基础入门:核心特点、五大数据类型与典型应用场景
很多人刚学 Redis 时,容易只背 SET、GET、HSET、LPUSH 这些命令。
但真正写项目时,更重要的问题其实是:
什么场景应该选什么 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 的 INCR、DECR、INCRBY 都是原子操作,适合做高并发计数。
# 阅读量 +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 的重点不是会多少命令,而是能不能根据业务场景选对数据结构。
参考资料
更多推荐
所有评论(0)