2021 字
10 分钟
Redis 完全指南 2026:核心数据结构 + 缓存设计模式 + 持久化 + 高可用集群
Redis 是现代后端架构中几乎不可缺少的一环——缓存、会话存储、消息队列、排行榜、分布式锁,它几乎无处不在。但很多开发者只会简单的 GET/SET,遇到缓存雪崩、数据一致性问题时就束手无策。
本文系统梳理 Redis 核心知识,从数据结构到高可用集群,配合实际可运行的代码和设计决策说明。
一、核心数据结构与使用场景
1.1 String(字符串)
# 基础操作SET user:1000:name "Alice"GET user:1000:name
# 带过期时间(EX=秒,PX=毫秒)SET session:abc123 "user_data" EX 3600
# 原子计数(点赞、访问量,无竞态)INCR article:42:viewsINCRBY article:42:views 100DECR article:42:views
# 分布式锁(NX=不存在则设置,EX=过期秒数)SET lock:order:1001 "worker-uuid" NX EX 30
# 批量操作MSET user:1:name Alice user:2:name BobMGET user:1:name user:2:name适用场景:缓存对象(JSON 序列化)、计数器、分布式锁、会话 token、限流计数
1.2 Hash(哈希)
# 存储对象(类似数据库一行记录)HSET user:1000 name Alice email alice@example.com age 30HGET user:1000 nameHMGET user:1000 name emailHGETALL user:1000
# 原子更新单个字段(无需序列化整个对象)HSET user:1000 last_login "2026-08-03"HINCRBY user:1000 login_count 1HDEL user:1000 age适用场景:用户/商品详情、购物车(field=商品ID,value=数量)
1.3 List(列表)
# 消息队列LPUSH queue:tasks '{"task":"send_email","to":"alice@example.com"}'BRPOP queue:tasks 30 # 阻塞弹出,最多等30秒
# 最近 N 条记录(固定长度)LPUSH user:1000:history "/page/about"LTRIM user:1000:history 0 49 # 保留最近50条
# 分页获取LRANGE user:1000:history 0 9 # 最近10条LLEN user:1000:history适用场景:消息队列(简单)、最近浏览记录、时间线
1.4 Set(集合)
# 标签系统SADD article:42:tags python backend redisSADD user:1000:interests python devops
# 集合运算SINTER article:42:tags user:1000:interests # 交集(共同标签)SUNION article:42:tags user:1000:interests # 并集SDIFF user:2:follows user:1:follows # 差集(推荐关注)
# 成员判断SISMEMBER online:users user:1000SCARD online:users # 集合大小适用场景:标签/兴趣、关注/粉丝(共同好友)、签到去重
1.5 Sorted Set(有序集合)
# 排行榜ZADD leaderboard 9850 "Alice"ZADD leaderboard 10000 "Carol"
# Top 3(分数从高到低)ZREVRANGE leaderboard 0 2 WITHSCORES
# 用户排名(0-indexed)ZREVRANK leaderboard "Alice"
# 原子增加分数ZINCRBY leaderboard 500 "Alice"
# 按分数范围查询(滑动窗口限流)ZRANGEBYSCORE ratelimit:user:1000 1722668400 +infZCOUNT ratelimit:user:1000 1722668400 +infZREMRANGEBYSCORE ratelimit:user:1000 -inf 1722668400适用场景:排行榜、延迟队列(score=执行时间戳)、滑动窗口限流
1.6 HyperLogLog(基数估计)
# UV 统计(误差约 0.81%,固定 12KB 内存)PFADD uv:2026-08-03 user:1000 user:1001 user:1002PFADD uv:2026-08-03 user:1000 # 重复不计PFCOUNT uv:2026-08-03 # 约 3
# 合并多天 UVPFMERGE uv:week uv:2026-08-01 uv:2026-08-02 uv:2026-08-03PFCOUNT uv:week适用场景:UV 统计、大规模去重计数(内存比 Set 少 90%+)
1.7 Stream(事件流)
# 生产者XADD events:orders * product_id 42 user_id 1000 action purchase
# 消费者组XGROUP CREATE events:orders group1 $ MKSTREAMXREADGROUP GROUP group1 consumer1 COUNT 10 BLOCK 2000 STREAMS events:orders >
# 确认消费XACK events:orders group1 <message-id>适用场景:事件溯源、消息队列(支持消费者组、消息确认,比 List 更完整)
二、缓存设计模式与问题解决
2.1 Cache Aside(旁路缓存,最常用)
import redis.asyncio as aioredisimport json
redis_client = aioredis.from_url("redis://localhost:6379", decode_responses=True)
async def get_user(user_id: int) -> dict | None: cache_key = f"user:{user_id}"
# 1. 查缓存 cached = await redis_client.get(cache_key) if cached: return json.loads(cached)
# 2. 缓存未命中,查数据库 user = await db.fetch_one("SELECT * FROM users WHERE id = ?", user_id) if user is None: # 缓存空值,防止穿透(短 TTL) await redis_client.set(cache_key, "null", ex=60) return None
# 3. 写入缓存 await redis_client.set(cache_key, json.dumps(dict(user)), ex=3600) return dict(user)
async def update_user(user_id: int, data: dict) -> None: await db.execute("UPDATE users SET ... WHERE id = ?", user_id) # 更新数据库后删缓存(下次读时重建),不要直接更新缓存! await redis_client.delete(f"user:{user_id}")2.2 缓存雪崩:TTL 抖动
import random
async def set_cache(key: str, value: str, base_ttl: int = 3600): # 加随机偏移(±10%),避免大量 key 同时过期 jitter = random.randint(-base_ttl // 10, base_ttl // 10) await redis_client.set(key, value, ex=base_ttl + jitter)2.3 缓存击穿:分布式互斥锁
import asyncio, uuid
async def get_hot_data(key: str) -> dict: cached = await redis_client.get(key) if cached: return json.loads(cached)
lock_key = f"lock:{key}" lock_val = str(uuid.uuid4())
# NX:不存在才设置(只有一个请求拿到锁) acquired = await redis_client.set(lock_key, lock_val, nx=True, ex=10) if acquired: try: cached = await redis_client.get(key) # 双重检查 if cached: return json.loads(cached)
data = await db.fetch_data(key) await redis_client.set(key, json.dumps(data), ex=3600) return data finally: # Lua 脚本原子释放锁(只释放自己的锁) script = """if redis.call("get",KEYS[1])==ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end""" await redis_client.eval(script, 1, lock_key, lock_val) else: await asyncio.sleep(0.1) return await get_hot_data(key)2.4 缓存穿透:布隆过滤器
# 初始化布隆过滤器(100万元素,误判率 0.1%)await redis_client.execute_command("BF.RESERVE", "user_bloom", 0.001, 1_000_000)
async def create_user(user_id: int, data: dict): await db.insert(user_id, data) # 同步到布隆过滤器 await redis_client.execute_command("BF.ADD", "user_bloom", user_id)
async def get_user_safe(user_id: int) -> dict | None: # 布隆过滤器说不存在 → 一定不存在,直接返回 if not await redis_client.execute_command("BF.EXISTS", "user_bloom", user_id): return None return await get_user(user_id)三、持久化策略
对比速查
| 策略 | 数据安全 | 性能影响 | 恢复速度 | 推荐场景 |
|---|---|---|---|---|
| RDB only | 最差(分钟级丢失) | 最小 | 最快 | 可接受数据丢失的缓存 |
| AOF only | 最好(最多1秒) | 中等 | 慢 | 数据安全优先 |
| RDB + AOF | 好(最多1秒) | 小 | 快 | 生产推荐 |
| 无持久化 | 无(重启全丢) | 无 | N/A | 纯缓存(可重建) |
# /etc/redis/redis.conf(生产推荐配置)
# RDB 快照save 900 1save 300 10save 60 10000
# AOFappendonly yesappendfsync everysec # 每秒 fsync(推荐,最多丢1秒数据)no-appendfsync-on-rewrite yes
# 混合持久化(Redis 4.0+)aof-use-rdb-preamble yes # AOF 重写时前段用 RDB 格式,加快恢复
# 内存淘汰策略maxmemory 4gbmaxmemory-policy allkeys-lru # 淘汰最久未使用的 key(缓存场景推荐)四、高可用方案
4.1 Redis Sentinel(哨兵)
主从架构:Master → Slave × 2哨兵监控:Sentinel × 3(奇数,防脑裂)主节点故障 → 哨兵选举 → 从节点升主 → 客户端更新连接# sentinel.confsentinel monitor mymaster 192.168.1.10 6379 2 # 2个哨兵同意才切换sentinel down-after-milliseconds mymaster 5000sentinel failover-timeout mymaster 10000
redis-sentinel /etc/redis/sentinel.conffrom redis.sentinel import Sentinel
sentinel = Sentinel( [("sentinel1", 26379), ("sentinel2", 26379), ("sentinel3", 26379)], socket_timeout=0.1,)master = sentinel.master_for("mymaster") # 写操作slave = sentinel.slave_for("mymaster") # 读操作(负载均衡)4.2 Redis Cluster(分片集群)
最小配置:3主节点(16384个哈希槽均分)+ 每主1从数据路由:客户端计算 HASH_SLOT(key) = CRC16(key) % 16384# 创建集群(生产需要真实IP)redis-cli --cluster create \ 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \ 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \ --cluster-replicas 1
redis-cli -p 7000 cluster inforedis-cli -p 7000 cluster nodesfrom redis.cluster import RedisCluster
client = RedisCluster( host="127.0.0.1", port=7000, decode_responses=True, skip_full_coverage_check=True,)五、实战代码
5.1 滑动窗口限流
async def is_rate_limited(user_id: str, limit: int = 100, window: int = 60) -> bool: key = f"ratelimit:{user_id}" now = time.time()
pipe = redis_client.pipeline() pipe.zremrangebyscore(key, "-inf", now - window) # 删除窗口外记录 pipe.zadd(key, {str(uuid.uuid4()): now}) # 记录本次请求 pipe.zcard(key) # 窗口内请求数 pipe.expire(key, window + 1) results = await pipe.execute()
return results[2] > limit # 超出限制返回 True5.2 延迟队列
async def schedule_task(task_data: dict, delay: int) -> None: """delay 秒后执行""" await redis_client.zadd( "delayed_queue", {json.dumps(task_data): time.time() + delay} )
async def poll_delayed_tasks(): while True: now = time.time() tasks = await redis_client.zrangebyscore("delayed_queue", "-inf", now) for task_json in tasks: if await redis_client.zrem("delayed_queue", task_json): # 原子删除防重复 await process_task(json.loads(task_json)) await asyncio.sleep(0.5)5.3 Session 存储(滑动过期)
async def create_session(user_id: int) -> str: sid = str(uuid.uuid4()) await redis_client.setex(f"session:{sid}", 86400, json.dumps({"user_id": user_id})) return sid
async def get_session(sid: str) -> dict | None: data = await redis_client.get(f"session:{sid}") if not data: return None await redis_client.expire(f"session:{sid}", 86400) # 每次访问刷新 TTL return json.loads(data)六、常用命令速查
# ── 通用 ──────────────────────────────────────────────────────SCAN 0 MATCH user:* COUNT 100 # 生产用 SCAN,禁用 KEYSTTL key # 剩余 TTL(-1=永久,-2=不存在)PERSIST key # 移除过期时间TYPE key # 数据类型UNLINK key # 异步删除(大 key 用这个)
# ── 监控与调试 ────────────────────────────────────────────────INFO memory # 内存详情INFO replication # 主从状态SLOWLOG GET 10 # 最近10条慢查询OBJECT ENCODING key # 内部编码MONITOR # 实时命令监控(仅调试,勿用于生产)
# ── 管理 ──────────────────────────────────────────────────────BGSAVE # 后台保存 RDBBGREWRITEAOF # 后台重写 AOFCONFIG GET maxmemoryCONFIG SET maxmemory 4gb # 动态修改FLUSHDB # 清空当前库(危险)FLUSHALL # 清空所有库(极危险)相关文章:
- Python 异步编程完全指南 2026:asyncio + aiohttp + 并发
- FastAPI 完全指南 2026:从零构建高性能异步 Python API
- Docker 完全指南 2026:Compose 多服务编排与生产部署
- Nginx 进阶完全指南 2026:反向代理 + HTTPS + 负载均衡
- Kubernetes 入门完全指南 2026:核心概念 + kubectl + Helm
本文基于 Redis 7.2.x 验证。Redis 8.0 正式引入向量搜索(Vector Search),AI/RAG 应用场景可关注后续更新。
Redis 完全指南 2026:核心数据结构 + 缓存设计模式 + 持久化 + 高可用集群
https://971918.xyz/posts/docs/redis-complete-guide/