一、Redis 是什么?
Redis 全称 Remote Dictionary Server ,是一个开源的内存数据库。与 MySQL 这类把数据存在磁盘上的数据库不同,Redis 把数据存在内存中,因此读写速度极快,通常可以达到每秒数十万次操作。
它的主要特点:
- 速度极快:基于内存操作,读写延迟在微秒级
- 数据结构丰富:不只是简单的 key-value,支持字符串、列表、哈希、集合、有序集合等
- 支持持久化:可以将内存数据定期保存到磁盘,防止重启后数据丢失
- 支持过期时间:每个 key 可以设置 TTL,到期自动删除
- 单线程模型:命令执行是串行的,天然避免并发写入冲突
二、核心数据结构
Redis 不是只会存字符串,它有 5 种基础数据类型,每种都针对特定场景设计。
1. String(字符串)
最基本的类型,可以存文本、数字、甚至序列化后的 JSON。
SET username "张三"
GET username # 返回 "张三"
SET counter 0
INCR counter # 自增,返回 1,常用于计数器
EXPIRE username 3600 # 设置 1 小时后过期
2. Hash(哈希)
类似一个小型 Map,适合存储对象。
HSET user:1 name "张三" age 28 city "北京"
HGET user:1 name # 返回 "张三"
HGETALL user:1 # 返回所有字段
3. List(列表)
有序的字符串列表,支持从头部或尾部插入/弹出,天然适合做队列。
RPUSH task "任务A" # 从右侧入队
RPUSH task "任务B"
LPOP task # 从左侧出队,返回 "任务A"
4. Set(集合)
无序、不重复的字符串集合,支持交集、并集、差集操作。
SADD online_users "user1" "user2" "user3"
SISMEMBER online_users "user1" # 判断是否在线
SCARD online_users # 在线人数
5. ZSet(有序集合)
在 Set 基础上,每个元素附带一个分数(score),按分数自动排序。排行榜的核心就靠它。
ZADD leaderboard 9800 "玩家A"
ZADD leaderboard 12000 "玩家B"
ZREVRANGE leaderboard 0 9 WITHSCORES # 取前10名,从高到低
三、典型场景一:缓存
这是 Redis 最经典的用途。当用户频繁请求同一份数据(比如商品详情页),每次都去查数据库既慢又浪费资源。解决思路是:第一次查数据库,结果存入 Redis,后续直接从 Redis 读取。
用户请求 → 查 Redis
↓ 命中?
是 → 直接返回(毫秒级)
否 → 查 MySQL → 写入 Redis → 返回
伪代码示意(Java/Python 风格):
def get_product(product_id):
cache_key = f"product:{product_id}"
# 先查缓存
result = redis.get(cache_key)
if result:
return json.loads(result)
# 缓存未命中,查数据库
product = db.query("SELECT * FROM products WHERE id = ?", product_id)
# 写入缓存,设置 10 分钟过期
redis.setex(cache_key, 600, json.dumps(product))
return product
注意几个常见问题:
- 缓存穿透:查询一个根本不存在的 key,每次都打到数据库。解决:对空结果也缓存,或使用布隆过滤器。
- 缓存击穿:某个热点 key 突然过期,大量请求同时打到数据库。解决:设置互斥锁,只让一个请求去查库。
- 缓存雪崩:大量 key 同时过期。解决:过期时间加随机抖动。
四、典型场景二:消息队列
利用 List 的 LPUSH / RPOP 特性,可以实现一个简单的消息队列,将耗时任务异步处理。
场景举例:用户注册后发送欢迎邮件。发邮件可能需要几秒,不能让用户等着。
用户注册 → 将"发邮件任务"RPUSH 进 Redis List
↓
后台 Worker 循环 LPOP 取任务 → 发送邮件
# 生产者:注册时将任务入队
def register(user):
db.save(user)
redis.rpush("email_queue", json.dumps({"to": user.email, "type": "welcome"}))
# 消费者:后台 Worker 持续处理
while True:
task = redis.blpop("email_queue", timeout=5) # 阻塞式取任务
if task:
send_email(json.loads(task[1]))
注意:Redis 的 List 队列适合轻量任务。如果业务复杂、需要消息确认、重试机制,建议使用 RabbitMQ 或 Kafka。
五、典型场景三:排行榜
ZSet(有序集合)天生为排行榜而生。score 存分数,member 存用户 ID,Redis 自动维护排序。
# 更新分数(游戏得分、销售额等)
ZINCRBY leaderboard 500 "user:1001" # user:1001 加 500 分
# 查询前 10 名
ZREVRANGE leaderboard 0 9 WITHSCORES
# 查询某个用户的排名
ZREVRANK leaderboard "user:1001" # 返回名次(从0开始)
# 查询某个用户的分数
ZSCORE leaderboard "user:1001"
这套操作的时间复杂度是 O(log N),即使有百万用户,查询依然飞快。
六、Redis 在项目中的典型架构位置
客户端
↓
后端服务(Spring Boot / Django 等)
↓ ↓
Redis MySQL
(缓存/队列) (持久存储)
Redis 不是用来替代 MySQL 的,而是配合数据库使用,承担高频读写的部分,减轻数据库压力。
总结
| 场景 | 用到的数据结构 | 核心命令 |
|---|---|---|
| 缓存 | String | SET / GET / SETEX |
| 消息队列 | List | RPUSH / BLPOP |
| 排行榜 | ZSet | ZADD / ZREVRANGE / ZREVRANK |
| 在线用户 | Set | SADD / SCARD |
| 存储对象 | Hash | HSET / HGETALL |
Redis 的学习曲线不陡,但要用好它,需要理解每种数据结构背后的设计意图。建议从缓存场景入手,在实际项目中逐步积累经验。