1. 什么是 Redis?
Redis(Remote Dictionary Server)是一个内存中的数据结构服务器。它不是关系型数据库,而是提供了 Key-Value 存储 + 丰富数据结构的内存中间件。
与传统数据库的区别
| 维度 |
MySQL |
Redis |
| 存储 |
磁盘(持久) |
内存(也可持久化) |
| 速度 |
毫秒级 |
微秒级(~100k ops/s) |
| 数据模型 |
表/行/列 |
String/Hash/List/Set/ZSet/Stream... |
| 查询方式 |
SQL |
基于 Key + 原子命令 |
| Schema |
严格 |
无 |
| 容量 |
TB/PB 级 |
受限于内存(GB 级) |
2. 五大核心数据结构深度解析
String — 万能的键值
SET session:abc123 "user:1001" EX 3600 # 会话存储(1小时过期)
INCR post:42:views # 文章浏览计数
SETBIT online_users 1001 1 # 位图:用户在线状态
Hash — 对象的完美载体
HSET product:2001 name "机械键盘" price 299 stock 50
HINCRBY product:2001 stock -1 # 扣库存(原子操作)
# 一个 Hash 可存储 40 亿字段,比 String 存储多个键节省内存
List — 队列与栈
LPUSH events:user:1001 "login" "view_homepage" "search"
LTRIM events:user:1001 0 99 # 只保留最近 100 条
# 阻塞队列(BRPOP)可实现消息队列
Set — 社交关系
SADD followers:1001 2001 2002 2003 # 我的粉丝
SADD following:1001 2002 2004 # 我关注的
SINTER followers:1001 following:1001 # 互关好友
SDIFF following:1001 followers:1001 # 我关注但没关注我的
Sorted Set — 排行榜引擎
ZADD leaderboard:weekly 95 "Alice" 92 "Bob" 88 "Charlie"
ZREVRANGE leaderboard:weekly 0 9 WITHSCORES # Top 10
ZRANK leaderboard:weekly "Alice" # Alice 的排名(0-based)
3. 缓存架构设计模式
Cache-Aside(旁路缓存)— 最常用
读:先查 Redis → 命中返回 | 未命中 → 查 DB → 回写 Redis
写:更新 DB → 删除/更新 Redis 缓存
def get_article(article_id):
cache_key = f"article:{article_id}"
# 1. 尝试缓存
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# 2. 查数据库
article = db.query("SELECT * FROM articles WHERE id = ?", article_id)
# 3. 回写缓存(TTL 5 分钟)
r.setex(cache_key, 300, json.dumps(article))
return article
缓存穿透 / 击穿 / 雪崩 解决方案
| 问题 |
现象 |
解决方案 |
| 穿透 |
查询不存在的数据,每次都打到 DB |
布隆过滤器 / 缓存空值 |
| 击穿 |
热点 Key 过期瞬间大量请求 |
互斥锁 / 永不过期 |
| 雪崩 |
大量 Key 同时过期 |
TTL 加随机值 / 多级缓存 |
4. 实战:实时排行榜
import redis
r = redis.Redis(decode_responses=True)
def record_score(game_id, user, score):
"""记录分数,保留最高分"""
key = f"leaderboard:{game_id}"
r.zadd(key, {user: score}) # ZADD 自动更新高分
def get_top_10(game_id):
key = f"leaderboard:{game_id}"
return r.zrevrange(key, 0, 9, withscores=True)
def get_user_rank(game_id, user):
"""返回排名(从1开始)"""
key = f"leaderboard:{game_id}"
rank = r.zrevrank(key, user)
if rank is None:
return None
return rank + 1
# 使用
record_score("tetris", "alice", 1500)
record_score("tetris", "bob", 1200)
record_score("tetris", "charlie", 1800)
for place, (user, score) in enumerate(get_top_10("tetris"), 1):
print(f"第{place}名: {user} - {int(score)}分")
print(f"Alice 排名: 第{get_user_rank('tetris', 'alice')}名")
5. 持久化策略
RDB:定时快照(适合备份/灾备,可能丢最后几分钟数据)
AOF:追加日志(更安全,everysec 策略丢最多 1 秒数据)
生产建议:同时开启 RDB + AOF
save 900 1
save 300 10
appendonly yes
appendfsync everysec
思考题
- Redis 单线程模型为什么能这么快?有哪些操作会阻塞主线程?
- 设计一个"限制每个用户每分钟最多发 5 条消息"的方案。
- 如果 Redis 宕机、所有缓存丢失,如何在不打垮 DB 的情况下恢复缓存?(预热策略)