从零入门到缓存架构实战

知识库
知识库文档
/tech-stacks/redis/tutorial/从零入门到缓存架构实战.md

文档

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
# redis.conf
save 900 1          # 15分钟内有1次修改则快照
save 300 10
appendonly yes
appendfsync everysec

思考题

  1. Redis 单线程模型为什么能这么快?有哪些操作会阻塞主线程?
  2. 设计一个"限制每个用户每分钟最多发 5 条消息"的方案。
  3. 如果 Redis 宕机、所有缓存丢失,如何在不打垮 DB 的情况下恢复缓存?(预热策略)

信息

路径
/tech-stacks/redis/tutorial/从零入门到缓存架构实战.md
更新时间
2026/5/31