Redis 数据结构实战教程:从五种基础类型到生产场景

2026-06-12 14:22:54

Redis 数据结构实战教程

适用读者:后端开发者,希望系统掌握 Redis 数据结构与生产实践。
预计学习时长:45 分钟 · 含场景实战

学习目标

完成本教程后,你将能够:

  1. 熟练使用 Redis 五种基础数据类型及其底层应用场景
  2. 设计合理的缓存 Key 命名规范与 TTL 策略
  3. 用 Sorted Set 实现排行榜和延迟队列
  4. 用 Stream 实现轻量级消息队列
  5. 避免缓存穿透、击穿、雪崩三大经典问题

前置知识

  • 基本的命令行操作
  • 了解键值存储概念
  • 任意一门后端语言(用于客户端示例)

第一章:环境准备

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)
  • 限流降级

练习 / 作业

  1. 用 Hash 实现用户信息缓存,支持字段级更新。
  2. 用 Sorted Set 实现文章热度排行榜(按阅读量排序)。
  3. 用 Stream 实现一个简单的订单处理队列,含消费者组和 ACK。
  4. 模拟缓存穿透场景并用「缓存空值」方案解决。
  5. 进阶:用 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 做可靠消息队列。掌握这些数据结构及其适用场景,是后端工程师的必备技能。