Redis 数据结构实战教程:从五种基础类型到生产场景
Redis 数据结构实战教程
适用读者:后端开发者,希望系统掌握 Redis 数据结构与生产实践。
预计学习时长:45 分钟 · 含场景实战
学习目标
完成本教程后,你将能够:
- 熟练使用 Redis 五种基础数据类型及其底层应用场景
- 设计合理的缓存 Key 命名规范与 TTL 策略
- 用 Sorted Set 实现排行榜和延迟队列
- 用 Stream 实现轻量级消息队列
- 避免缓存穿透、击穿、雪崩三大经典问题
前置知识
- 基本的命令行操作
- 了解键值存储概念
- 任意一门后端语言(用于客户端示例)
第一章:环境准备
1.1 安装与连接
# Docker 快速启动
docker run -d --name redis -p 6379:6379 redis:7-alpine
# 连接
redis-cli
127.0.0.1:6379> PING
PONG
1.2 基本配置建议
# redis.conf 生产建议
maxmemory 256mb
maxmemory-policy allkeys-lru
appendonly yes
appendfsync everysec
第二章:String(字符串)
2.1 基本操作
SET user:1001:name "张三"
GET user:1001:name
# "张三"
SET session:abc123 "user_id=1001" EX 3600
# EX 3600 = 3600 秒后过期
INCR page:views:homepage
# 原子自增,适合计数器
MSET config:site:name "我的博客" config:site:url "https://example.com"
MGET config:site:name config:site:url
2.2 场景:分布式锁
# 加锁(NX = 不存在才设置,EX = 过期秒数)
SET lock:order:12345 "worker-1" NX EX 30
# 释放锁(Lua 脚本保证原子性,防止误删他人锁)
-- release_lock.lua
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
2.3 场景:缓存对象
SET user:1001:profile '{"name":"张三","level":"vip"}' EX 1800
生产环境推荐用 Hash 代替大 JSON String,便于部分更新。
第三章:Hash(哈希)
3.1 基本操作
HSET user:1001 name "张三" email "zhang@example.com" level "vip"
HGET user:1001 name
# "张三"
HGETALL user:1001
# 1) "name" 2) "张三" 3) "email" 4) "zhang@example.com" 5) "level" 6) "vip"
HMGET user:1001 name email
HINCRBY user:1001 login_count 1
3.2 场景:用户信息缓存
Hash 的优势:可单独更新 login_count 而不重写整个对象。
# Python 示例
import redis
r = redis.Redis()
def get_user(user_id):
key = f"user:{user_id}"
data = r.hgetall(key)
if data:
return data
user = db.query(user_id) # 回源数据库
r.hset(key, mapping=user)
r.expire(key, 1800)
return user
第四章:List(列表)
4.1 基本操作
LPUSH queue:email "job1" "job2" "job3"
RPOP queue:email
# "job1"(FIFO 队列)
LRANGE queue:email 0 -1
# 查看全部
BLPOP queue:email 10
# 阻塞弹出,超时 10 秒
4.2 场景:简单消息队列
# 生产者
LPUSH task:queue '{"type":"send_email","to":"user@example.com"}'
# 消费者(阻塞等待)
BLPOP task:queue 0
注意:List 做队列无 ACK 机制,消费者崩溃可能丢消息。生产环境推荐 Stream。
4.3 场景:最新 N 条记录
LPUSH news:latest "article:101" "article:102"
LTRIM news:latest 0 9
# 只保留最新 10 条
第五章:Set(集合)
5.1 基本操作
SADD tags:article:101 "redis" "cache" "database"
SMEMBERS tags:article:101
SADD user:1001:followers 2001 2002 2003
SISMEMBER user:1001:followers 2001
# 1(存在)
# 交集:共同关注
SINTER user:1001:followers user:2001:followers
# 并集
SUNION tags:article:101 tags:article:102
5.2 场景:去重与判重
# 用户是否已点赞
SADD article:101:likes 1001
# 返回 1 表示新增成功(首次点赞),0 表示已存在
# 每日 UV 统计
SADD uv:2026-06-12 "user:1001"
SCARD uv:2026-06-12
第六章:Sorted Set(有序集合)
6.1 基本操作
ZADD leaderboard 9500 "player:张三" 8800 "player:李四" 10200 "player:王五"
ZREVRANGE leaderboard 0 9 WITHSCORES
# 1) "player:王五" 2) "10200" 3) "player:张三" 4) "9500"
ZREVRANK leaderboard "player:张三"
# 排名(0-based)
ZINCRBY leaderboard 100 "player:张三"
6.2 场景:排行榜
# 游戏积分榜
ZADD game:rank:2026Q2 15000 "uid:1001" 12000 "uid:1002"
# Top 10
ZREVRANGE game:rank:2026Q2 0 9 WITHSCORES
# 某用户排名
ZREVRANK game:rank:2026Q2 "uid:1001"
6.3 场景:延迟队列
# score = 执行时间戳
ZADD delay:queue 1718182800 '{"task":"send_reminder","user":1001}'
# 消费者:取出到期的任务
ZRANGEBYSCORE delay:queue 0 $(date +%s) LIMIT 0 10
第七章:Stream 与高级类型
7.1 Stream 消息队列
# 生产者
XADD orders:stream * order_id 12345 amount 99.9
# 消费者组
XGROUP CREATE orders:stream order-processors $ MKSTREAM
XREADGROUP GROUP order-processors consumer-1 COUNT 10 BLOCK 5000 STREAMS orders:stream >
# 处理完毕后 ACK
XACK orders:stream order-processors <message-id>
Stream 支持消费者组、ACK、Pending 重试,是 Redis 官方推荐的队列方案。
7.2 HyperLogLog(基数统计)
PFADD uv:hyperlog:2026-06-12 "user:1001" "user:1002" "user:1001"
PFCOUNT uv:hyperlog:2026-06-12
# 2(去重后,误差率 0.81%)
海量 UV 统计仅需 12KB 内存。
第八章:缓存问题与对策
8.1 缓存穿透
查询不存在的数据,缓存和 DB 都没有,每次打到 DB。
对策:
# 缓存空值
SET user:99999:profile "NULL" EX 300
# 或布隆过滤器(客户端/Redis Module)
8.2 缓存击穿
热点 Key 过期瞬间,大量请求打到 DB。
对策:
# 互斥锁
if not r.exists(key):
if r.set(lock_key, "1", nx=True, ex=10):
data = load_from_db()
r.set(key, data, ex=1800)
r.delete(lock_key)
else:
time.sleep(0.1)
return get_cached(key)
8.3 缓存雪崩
大量 Key 同时过期。
对策:
- TTL 加随机偏移:
EX 1800 + random(300) - 多级缓存(本地 + Redis)
- 限流降级
练习 / 作业
- 用 Hash 实现用户信息缓存,支持字段级更新。
- 用 Sorted Set 实现文章热度排行榜(按阅读量排序)。
- 用 Stream 实现一个简单的订单处理队列,含消费者组和 ACK。
- 模拟缓存穿透场景并用「缓存空值」方案解决。
- 进阶:用 Redis + Lua 实现限流器(滑动窗口算法)。
FAQ
Q:Redis 单线程为什么这么快?
A:纯内存操作 + IO 多路复用 + 高效数据结构。Redis 6.0+ 网络 IO 可多线程,命令执行仍单线程。
Q:String 和 Hash 存对象怎么选?
A:对象字段少且需部分更新用 Hash;简单 KV 或需整体过期用 String。
Q:List 和 Stream 做队列怎么选?
A:简单场景用 List;需要 ACK、消费组、回溯用 Stream。
Q:生产环境持久化怎么配?
A:推荐 AOF(appendfsync everysec),兼顾性能与安全。RDB 做冷备份。
Q:Key 命名规范?
A:推荐 业务:对象:ID:属性,如 order:12345:detail。避免过长 Key。
小结
Redis 不仅是缓存,更是多功能数据结构服务器。String 做计数和锁,Hash 存对象,List 做轻量队列,Set 做去重和交集,Sorted Set 做排行榜和延迟队列,Stream 做可靠消息队列。掌握这些数据结构及其适用场景,是后端工程师的必备技能。