前言
💡 痛点 :Redis 只会
SET/GET?缓存穿透、击穿、雪崩怎么处理?消息队列用 Redis 靠谱吗?分布式锁怎么实现才安全?🎯 解决方案 :从基础到生产级,系统讲解 Redis 7 在缓存、消息队列、分布式锁三大场景的实战方案。
Redis 7 是当前最稳定的 Redis 版本,引入了 Function、ACL 改进等新特性。本文将覆盖:
#mermaid-svg-v5lQpsxLxHGbB2at{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-v5lQpsxLxHGbB2at .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-v5lQpsxLxHGbB2at .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-v5lQpsxLxHGbB2at .error-icon{fill:#552222;}#mermaid-svg-v5lQpsxLxHGbB2at .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-v5lQpsxLxHGbB2at .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-v5lQpsxLxHGbB2at .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-v5lQpsxLxHGbB2at .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-v5lQpsxLxHGbB2at .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-v5lQpsxLxHGbB2at .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-v5lQpsxLxHGbB2at .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-v5lQpsxLxHGbB2at .marker{fill:#333333;stroke:#333333;}#mermaid-svg-v5lQpsxLxHGbB2at .marker.cross{stroke:#333333;}#mermaid-svg-v5lQpsxLxHGbB2at svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-v5lQpsxLxHGbB2at p{margin:0;}#mermaid-svg-v5lQpsxLxHGbB2at .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-v5lQpsxLxHGbB2at .cluster-label text{fill:#333;}#mermaid-svg-v5lQpsxLxHGbB2at .cluster-label span{color:#333;}#mermaid-svg-v5lQpsxLxHGbB2at .cluster-label span p{background-color:transparent;}#mermaid-svg-v5lQpsxLxHGbB2at .label text,#mermaid-svg-v5lQpsxLxHGbB2at span{fill:#333;color:#333;}#mermaid-svg-v5lQpsxLxHGbB2at .node rect,#mermaid-svg-v5lQpsxLxHGbB2at .node circle,#mermaid-svg-v5lQpsxLxHGbB2at .node ellipse,#mermaid-svg-v5lQpsxLxHGbB2at .node polygon,#mermaid-svg-v5lQpsxLxHGbB2at .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-v5lQpsxLxHGbB2at .rough-node .label text,#mermaid-svg-v5lQpsxLxHGbB2at .node .label text,#mermaid-svg-v5lQpsxLxHGbB2at .image-shape .label,#mermaid-svg-v5lQpsxLxHGbB2at .icon-shape .label{text-anchor:middle;}#mermaid-svg-v5lQpsxLxHGbB2at .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-v5lQpsxLxHGbB2at .rough-node .label,#mermaid-svg-v5lQpsxLxHGbB2at .node .label,#mermaid-svg-v5lQpsxLxHGbB2at .image-shape .label,#mermaid-svg-v5lQpsxLxHGbB2at .icon-shape .label{text-align:center;}#mermaid-svg-v5lQpsxLxHGbB2at .node.clickable{cursor:pointer;}#mermaid-svg-v5lQpsxLxHGbB2at .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-v5lQpsxLxHGbB2at .arrowheadPath{fill:#333333;}#mermaid-svg-v5lQpsxLxHGbB2at .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-v5lQpsxLxHGbB2at .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-v5lQpsxLxHGbB2at .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-v5lQpsxLxHGbB2at .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-v5lQpsxLxHGbB2at .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-v5lQpsxLxHGbB2at .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-v5lQpsxLxHGbB2at .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-v5lQpsxLxHGbB2at .cluster text{fill:#333;}#mermaid-svg-v5lQpsxLxHGbB2at .cluster span{color:#333;}#mermaid-svg-v5lQpsxLxHGbB2at div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-v5lQpsxLxHGbB2at .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-v5lQpsxLxHGbB2at rect.text{fill:none;stroke-width:0;}#mermaid-svg-v5lQpsxLxHGbB2at .icon-shape,#mermaid-svg-v5lQpsxLxHGbB2at .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-v5lQpsxLxHGbB2at .icon-shape p,#mermaid-svg-v5lQpsxLxHGbB2at .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-v5lQpsxLxHGbB2at .icon-shape .label rect,#mermaid-svg-v5lQpsxLxHGbB2at .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-v5lQpsxLxHGbB2at .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-v5lQpsxLxHGbB2at .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-v5lQpsxLxHGbB2at :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🚀 Redis 7 实战
📦 缓存实战
📨 消息队列实战
🔒 分布式锁实战
⚙️ 生产级配置
基础缓存
三大问题解决
多级缓存
Pub/Sub
Stream
延迟队列
基础锁
Redisson
红锁算法
内存优化
持久化
集群方案
一、Redis 7 核心特性回顾
1.1 数据结构速查
#mermaid-svg-Cpg07YTdVUe2Cy69{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Cpg07YTdVUe2Cy69 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Cpg07YTdVUe2Cy69 .error-icon{fill:#552222;}#mermaid-svg-Cpg07YTdVUe2Cy69 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Cpg07YTdVUe2Cy69 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Cpg07YTdVUe2Cy69 .marker.cross{stroke:#333333;}#mermaid-svg-Cpg07YTdVUe2Cy69 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Cpg07YTdVUe2Cy69 p{margin:0;}#mermaid-svg-Cpg07YTdVUe2Cy69 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Cpg07YTdVUe2Cy69 .cluster-label text{fill:#333;}#mermaid-svg-Cpg07YTdVUe2Cy69 .cluster-label span{color:#333;}#mermaid-svg-Cpg07YTdVUe2Cy69 .cluster-label span p{background-color:transparent;}#mermaid-svg-Cpg07YTdVUe2Cy69 .label text,#mermaid-svg-Cpg07YTdVUe2Cy69 span{fill:#333;color:#333;}#mermaid-svg-Cpg07YTdVUe2Cy69 .node rect,#mermaid-svg-Cpg07YTdVUe2Cy69 .node circle,#mermaid-svg-Cpg07YTdVUe2Cy69 .node ellipse,#mermaid-svg-Cpg07YTdVUe2Cy69 .node polygon,#mermaid-svg-Cpg07YTdVUe2Cy69 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Cpg07YTdVUe2Cy69 .rough-node .label text,#mermaid-svg-Cpg07YTdVUe2Cy69 .node .label text,#mermaid-svg-Cpg07YTdVUe2Cy69 .image-shape .label,#mermaid-svg-Cpg07YTdVUe2Cy69 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Cpg07YTdVUe2Cy69 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Cpg07YTdVUe2Cy69 .rough-node .label,#mermaid-svg-Cpg07YTdVUe2Cy69 .node .label,#mermaid-svg-Cpg07YTdVUe2Cy69 .image-shape .label,#mermaid-svg-Cpg07YTdVUe2Cy69 .icon-shape .label{text-align:center;}#mermaid-svg-Cpg07YTdVUe2Cy69 .node.clickable{cursor:pointer;}#mermaid-svg-Cpg07YTdVUe2Cy69 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Cpg07YTdVUe2Cy69 .arrowheadPath{fill:#333333;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Cpg07YTdVUe2Cy69 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cpg07YTdVUe2Cy69 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Cpg07YTdVUe2Cy69 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cpg07YTdVUe2Cy69 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Cpg07YTdVUe2Cy69 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Cpg07YTdVUe2Cy69 .cluster text{fill:#333;}#mermaid-svg-Cpg07YTdVUe2Cy69 .cluster span{color:#333;}#mermaid-svg-Cpg07YTdVUe2Cy69 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Cpg07YTdVUe2Cy69 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Cpg07YTdVUe2Cy69 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Cpg07YTdVUe2Cy69 .icon-shape,#mermaid-svg-Cpg07YTdVUe2Cy69 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cpg07YTdVUe2Cy69 .icon-shape p,#mermaid-svg-Cpg07YTdVUe2Cy69 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Cpg07YTdVUe2Cy69 .icon-shape .label rect,#mermaid-svg-Cpg07YTdVUe2Cy69 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cpg07YTdVUe2Cy69 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Cpg07YTdVUe2Cy69 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Cpg07YTdVUe2Cy69 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Redis 数据结构
String
字符串
Hash
哈希
List
列表
Set
集合
ZSet
有序集合
Stream
流
数据结构选型指南:
| 数据结构 | 时间复杂度 | 典型场景 |
|---|---|---|
| String | O(1) | 缓存、计数器、分布式锁 |
| Hash | O(1) | 对象存储、购物车 |
| List | O(n) | 消息队列、最新列表 |
| Set | O(1) | 去重、标签、共同好友 |
| ZSet | O(log n) | 排行榜、延迟队列 |
| Stream | O(1) | 消息队列、事件溯源 |
1.2 Redis 7 新特性
| 特性 | 说明 | 影响 |
|---|---|---|
| Functions | 替代 Lua 脚本,更好管理 | 复杂逻辑 |
| ACL v2 | 细粒度权限控制 | 安全性 |
| Sharded Pub/Sub | 分片 Pub/Sub | 扩展性 |
| Multi-part AOF | AOF 优化 | 性能 + 可靠性 |
| CIDR ACL | 支持 IP 段权限 | 运维 |
二、缓存实战
2.1 基础缓存模式
🗄️ 数据库 📦 Redis 🖥️ 应用 🖥️ 客户端 🗄️ 数据库 📦 Redis 🖥️ 应用 🖥️ 客户端 #mermaid-svg-du2EUI3gypgjnNqQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-du2EUI3gypgjnNqQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-du2EUI3gypgjnNqQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-du2EUI3gypgjnNqQ .error-icon{fill:#552222;}#mermaid-svg-du2EUI3gypgjnNqQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-du2EUI3gypgjnNqQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-du2EUI3gypgjnNqQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-du2EUI3gypgjnNqQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-du2EUI3gypgjnNqQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-du2EUI3gypgjnNqQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-du2EUI3gypgjnNqQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-du2EUI3gypgjnNqQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-du2EUI3gypgjnNqQ .marker.cross{stroke:#333333;}#mermaid-svg-du2EUI3gypgjnNqQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-du2EUI3gypgjnNqQ p{margin:0;}#mermaid-svg-du2EUI3gypgjnNqQ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-du2EUI3gypgjnNqQ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-du2EUI3gypgjnNqQ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-du2EUI3gypgjnNqQ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-du2EUI3gypgjnNqQ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-du2EUI3gypgjnNqQ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-du2EUI3gypgjnNqQ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-du2EUI3gypgjnNqQ .sequenceNumber{fill:white;}#mermaid-svg-du2EUI3gypgjnNqQ #sequencenumber{fill:#333;}#mermaid-svg-du2EUI3gypgjnNqQ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-du2EUI3gypgjnNqQ .messageText{fill:#333;stroke:none;}#mermaid-svg-du2EUI3gypgjnNqQ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-du2EUI3gypgjnNqQ .labelText,#mermaid-svg-du2EUI3gypgjnNqQ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-du2EUI3gypgjnNqQ .loopText,#mermaid-svg-du2EUI3gypgjnNqQ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-du2EUI3gypgjnNqQ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-du2EUI3gypgjnNqQ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-du2EUI3gypgjnNqQ .noteText,#mermaid-svg-du2EUI3gypgjnNqQ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-du2EUI3gypgjnNqQ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-du2EUI3gypgjnNqQ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-du2EUI3gypgjnNqQ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-du2EUI3gypgjnNqQ .actorPopupMenu{position:absolute;}#mermaid-svg-du2EUI3gypgjnNqQ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-du2EUI3gypgjnNqQ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-du2EUI3gypgjnNqQ .actor-man circle,#mermaid-svg-du2EUI3gypgjnNqQ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-du2EUI3gypgjnNqQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt 缓存命中 缓存未命中 GET cache_key 返回缓存值 响应 NULL SELECT * FROM ... 返回数据 SET cache_key value EX 3600 响应
Python 实现
python
"""
基础缓存读写
"""
import redis
import json
from typing import Optional, Any
from functools import wraps
# 连接 Redis
r = redis.Redis(
host='localhost',
port=6379,
db=0,
decode_responses=True,
socket_timeout=5,
retry_on_timeout=True,
health_check_interval=30
)
def cache_get(key: str) -> Optional[Any]:
"""获取缓存"""
value = r.get(key)
if value is None:
return None
try:
return json.loads(value)
except (json.JSONDecodeError, TypeError):
return value
def cache_set(key: str, value: Any, ttl: int = 3600):
"""设置缓存"""
serialized = json.dumps(value, ensure_ascii=False) if not isinstance(value, str) else value
r.setex(key, ttl, serialized)
# 缓存装饰器
def cached(key_prefix: str, ttl: int = 3600):
"""缓存装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 生成缓存 key
cache_key = f"{key_prefix}:{hash(str(args) + str(kwargs))}"
# 尝试读取缓存
cached_value = cache_get(cache_key)
if cached_value is not None:
return cached_value
# 执行函数
result = func(*args, **kwargs)
# 写入缓存
if result is not None:
cache_set(cache_key, result, ttl)
return result
return wrapper
return decorator
# 使用示例
@cached("user:info", ttl=1800)
def get_user_info(user_id: int) -> dict:
"""获取用户信息(自动缓存)"""
# 模拟数据库查询
db_result = {"id": user_id, "name": "张三", "age": 25}
return db_result
2.2 三大缓存问题与解决
#mermaid-svg-NE6sQFMKCztLCxcD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NE6sQFMKCztLCxcD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NE6sQFMKCztLCxcD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NE6sQFMKCztLCxcD .error-icon{fill:#552222;}#mermaid-svg-NE6sQFMKCztLCxcD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NE6sQFMKCztLCxcD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NE6sQFMKCztLCxcD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NE6sQFMKCztLCxcD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NE6sQFMKCztLCxcD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NE6sQFMKCztLCxcD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NE6sQFMKCztLCxcD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NE6sQFMKCztLCxcD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NE6sQFMKCztLCxcD .marker.cross{stroke:#333333;}#mermaid-svg-NE6sQFMKCztLCxcD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NE6sQFMKCztLCxcD p{margin:0;}#mermaid-svg-NE6sQFMKCztLCxcD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NE6sQFMKCztLCxcD .cluster-label text{fill:#333;}#mermaid-svg-NE6sQFMKCztLCxcD .cluster-label span{color:#333;}#mermaid-svg-NE6sQFMKCztLCxcD .cluster-label span p{background-color:transparent;}#mermaid-svg-NE6sQFMKCztLCxcD .label text,#mermaid-svg-NE6sQFMKCztLCxcD span{fill:#333;color:#333;}#mermaid-svg-NE6sQFMKCztLCxcD .node rect,#mermaid-svg-NE6sQFMKCztLCxcD .node circle,#mermaid-svg-NE6sQFMKCztLCxcD .node ellipse,#mermaid-svg-NE6sQFMKCztLCxcD .node polygon,#mermaid-svg-NE6sQFMKCztLCxcD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NE6sQFMKCztLCxcD .rough-node .label text,#mermaid-svg-NE6sQFMKCztLCxcD .node .label text,#mermaid-svg-NE6sQFMKCztLCxcD .image-shape .label,#mermaid-svg-NE6sQFMKCztLCxcD .icon-shape .label{text-anchor:middle;}#mermaid-svg-NE6sQFMKCztLCxcD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NE6sQFMKCztLCxcD .rough-node .label,#mermaid-svg-NE6sQFMKCztLCxcD .node .label,#mermaid-svg-NE6sQFMKCztLCxcD .image-shape .label,#mermaid-svg-NE6sQFMKCztLCxcD .icon-shape .label{text-align:center;}#mermaid-svg-NE6sQFMKCztLCxcD .node.clickable{cursor:pointer;}#mermaid-svg-NE6sQFMKCztLCxcD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NE6sQFMKCztLCxcD .arrowheadPath{fill:#333333;}#mermaid-svg-NE6sQFMKCztLCxcD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NE6sQFMKCztLCxcD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NE6sQFMKCztLCxcD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NE6sQFMKCztLCxcD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NE6sQFMKCztLCxcD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NE6sQFMKCztLCxcD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NE6sQFMKCztLCxcD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NE6sQFMKCztLCxcD .cluster text{fill:#333;}#mermaid-svg-NE6sQFMKCztLCxcD .cluster span{color:#333;}#mermaid-svg-NE6sQFMKCztLCxcD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NE6sQFMKCztLCxcD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NE6sQFMKCztLCxcD rect.text{fill:none;stroke-width:0;}#mermaid-svg-NE6sQFMKCztLCxcD .icon-shape,#mermaid-svg-NE6sQFMKCztLCxcD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NE6sQFMKCztLCxcD .icon-shape p,#mermaid-svg-NE6sQFMKCztLCxcD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NE6sQFMKCztLCxcD .icon-shape .label rect,#mermaid-svg-NE6sQFMKCztLCxcD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NE6sQFMKCztLCxcD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NE6sQFMKCztLCxcD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NE6sQFMKCztLCxcD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ⚠️ 缓存三大问题
穿透
Cache Penetration
击穿
Cache Breakdown
雪崩
Cache Avalanche
查询不存在的数据
方案:布隆过滤器
空值缓存
热点 key 过期
方案:互斥锁
永不过期 + 逻辑过期
大量 key 同时过期
方案:随机 TTL
多级缓存
问题 1:缓存穿透
场景:查询不存在的数据,每次都打到数据库。
python
"""
缓存穿透解决方案
"""
from pybloom_live import ScalableBloomFilter
# 方案 1:布隆过滤器
bloom = ScalableBloomFilter(initial_capacity=100000, error_rate=0.001)
def init_bloom_filter():
"""初始化布隆过滤器(加载所有存在的 key)"""
for user_id in db.query("SELECT id FROM users"):
bloom.add(f"user:{user_id}")
def get_user_with_bloom(user_id: int) -> Optional[dict]:
"""布隆过滤器 + 缓存"""
cache_key = f"user:{user_id}"
# 1. 布隆过滤器判断
if cache_key not in bloom:
return None # 一定不存在,直接返回
# 2. 查缓存
cached = cache_get(cache_key)
if cached is not None:
return cached
# 3. 查数据库
user = db.query("SELECT * FROM users WHERE id = ?", user_id)
if user:
cache_set(cache_key, user)
return user
return None
# 方案 2:空值缓存
def get_user_with_null_cache(user_id: int) -> Optional[dict]:
"""空值缓存防止穿透"""
cache_key = f"user:{user_id}"
cached = cache_get(cache_key)
if cached is not None:
if cached == "__NULL__":
return None # 空值缓存,说明不存在
return cached
user = db.query("SELECT * FROM users WHERE id = ?", user_id)
if user:
cache_set(cache_key, user, ttl=3600)
else:
# 缓存空值,短过期时间
cache_set(cache_key, "__NULL__", ttl=60)
return user
问题 2:缓存击穿
场景:热点 key 过期瞬间,大量并发打到数据库。
python
"""
缓存击穿解决方案
"""
import threading
lock_cache = {} # 简单锁缓存
def get_user_with_mutex(user_id: int) -> Optional[dict]:
"""互斥锁方案:只允许一个线程重建缓存"""
cache_key = f"user:{user_id}"
# 1. 查缓存
cached = cache_get(cache_key)
if cached is not None:
return cached
# 2. 获取分布式锁
lock_key = f"lock:{cache_key}"
# 使用 SET NX EX 实现简单分布式锁
acquired = r.set(lock_key, "1", nx=True, ex=10)
if acquired:
try:
# 双重检查:可能其他线程已经重建了缓存
cached = cache_get(cache_key)
if cached is not None:
return cached
# 查数据库
user = db.query("SELECT * FROM users WHERE id = ?", user_id)
if user:
cache_set(cache_key, user, ttl=3600)
return user
finally:
# 释放锁
r.delete(lock_key)
else:
# 获取锁失败,短暂等待后重试
import time
time.sleep(0.1)
return get_user_with_mutex(user_id) # 递归重试
return None
def get_user_with_logical_expire(user_id: int) -> dict:
"""逻辑过期方案:热点 key 永不过期"""
cache_key = f"user:{user_id}"
cached = cache_get(cache_key)
if cached is not None:
# 检查逻辑过期时间
if cached.get("_expire_at", 0) > time.time():
return cached["data"] # 未过期
# 逻辑过期,异步更新(不阻塞请求)
import threading
threading.Thread(
target=refresh_cache,
args=(user_id,)
).start()
return cached["data"] # 返回旧数据
# 首次加载
user = db.query("SELECT * FROM users WHERE id = ?", user_id)
cache_data = {
"data": user,
"_expire_at": time.time() + 3600
}
cache_set(cache_key, cache_data, ttl=86400 * 7) # 物理过期 7 天
return user
def refresh_cache(user_id: int):
"""异步刷新缓存"""
user = db.query("SELECT * FROM users WHERE id = ?", user_id)
cache_key = f"user:{user_id}"
cache_data = {
"data": user,
"_expire_at": time.time() + 3600
}
cache_set(cache_key, cache_data, ttl=86400 * 7)
问题 3:缓存雪崩
场景:大量 key 同时过期,数据库压力暴增。
python
"""
缓存雪崩解决方案
"""
import random
def cache_set_with_jitter(key: str, value: Any, base_ttl: int = 3600):
"""随机 TTL 防止同时过期"""
jitter = random.randint(-base_ttl // 10, base_ttl // 10)
actual_ttl = base_ttl + jitter
cache_set(key, value, actual_ttl)
# 方案对比
solutions = {
"随机TTL": "在基础 TTL 上加随机抖动,避免同时过期",
"多级缓存": "本地缓存 + Redis 缓存,即使 Redis 全挂,本地还能撑住",
"缓存预热": "系统启动时主动加载热点数据到缓存",
"熔断降级": "数据库压力过大时,暂时返回默认值",
}
2.3 多级缓存架构
#mermaid-svg-XtuKJWuHrHm0WTT3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XtuKJWuHrHm0WTT3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XtuKJWuHrHm0WTT3 .error-icon{fill:#552222;}#mermaid-svg-XtuKJWuHrHm0WTT3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XtuKJWuHrHm0WTT3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XtuKJWuHrHm0WTT3 .marker.cross{stroke:#333333;}#mermaid-svg-XtuKJWuHrHm0WTT3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XtuKJWuHrHm0WTT3 p{margin:0;}#mermaid-svg-XtuKJWuHrHm0WTT3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XtuKJWuHrHm0WTT3 .cluster-label text{fill:#333;}#mermaid-svg-XtuKJWuHrHm0WTT3 .cluster-label span{color:#333;}#mermaid-svg-XtuKJWuHrHm0WTT3 .cluster-label span p{background-color:transparent;}#mermaid-svg-XtuKJWuHrHm0WTT3 .label text,#mermaid-svg-XtuKJWuHrHm0WTT3 span{fill:#333;color:#333;}#mermaid-svg-XtuKJWuHrHm0WTT3 .node rect,#mermaid-svg-XtuKJWuHrHm0WTT3 .node circle,#mermaid-svg-XtuKJWuHrHm0WTT3 .node ellipse,#mermaid-svg-XtuKJWuHrHm0WTT3 .node polygon,#mermaid-svg-XtuKJWuHrHm0WTT3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XtuKJWuHrHm0WTT3 .rough-node .label text,#mermaid-svg-XtuKJWuHrHm0WTT3 .node .label text,#mermaid-svg-XtuKJWuHrHm0WTT3 .image-shape .label,#mermaid-svg-XtuKJWuHrHm0WTT3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-XtuKJWuHrHm0WTT3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XtuKJWuHrHm0WTT3 .rough-node .label,#mermaid-svg-XtuKJWuHrHm0WTT3 .node .label,#mermaid-svg-XtuKJWuHrHm0WTT3 .image-shape .label,#mermaid-svg-XtuKJWuHrHm0WTT3 .icon-shape .label{text-align:center;}#mermaid-svg-XtuKJWuHrHm0WTT3 .node.clickable{cursor:pointer;}#mermaid-svg-XtuKJWuHrHm0WTT3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XtuKJWuHrHm0WTT3 .arrowheadPath{fill:#333333;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XtuKJWuHrHm0WTT3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XtuKJWuHrHm0WTT3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XtuKJWuHrHm0WTT3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XtuKJWuHrHm0WTT3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XtuKJWuHrHm0WTT3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XtuKJWuHrHm0WTT3 .cluster text{fill:#333;}#mermaid-svg-XtuKJWuHrHm0WTT3 .cluster span{color:#333;}#mermaid-svg-XtuKJWuHrHm0WTT3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XtuKJWuHrHm0WTT3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XtuKJWuHrHm0WTT3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-XtuKJWuHrHm0WTT3 .icon-shape,#mermaid-svg-XtuKJWuHrHm0WTT3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XtuKJWuHrHm0WTT3 .icon-shape p,#mermaid-svg-XtuKJWuHrHm0WTT3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XtuKJWuHrHm0WTT3 .icon-shape .label rect,#mermaid-svg-XtuKJWuHrHm0WTT3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XtuKJWuHrHm0WTT3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XtuKJWuHrHm0WTT3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XtuKJWuHrHm0WTT3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 未命中
未命中
回填
回填
请求
L1: 本地缓存
Caffeine/本地Dict
L2: Redis
分布式缓存
L3: 数据库
python
"""
多级缓存实现
"""
from cachetools import TTLCache
# L1: 本地缓存
local_cache = TTLCache(maxsize=1000, ttl=60)
class MultiLevelCache:
"""多级缓存"""
def __init__(self, redis_client):
self.redis = redis_client
self.local = local_cache
def get(self, key: str) -> Optional[Any]:
"""逐级查询"""
# L1: 本地缓存
value = self.local.get(key)
if value is not None:
return value
# L2: Redis
value = self.redis.get(key)
if value is not None:
try:
parsed = json.loads(value)
except:
parsed = value
# 回填本地缓存
self.local[key] = parsed
return parsed
return None
def set(self, key: str, value: Any, redis_ttl: int = 3600):
"""写入多级缓存"""
self.local[key] = value # 本地缓存
serialized = json.dumps(value, ensure_ascii=False) if not isinstance(value, str) else value
self.redis.setex(key, redis_ttl, serialized) # Redis
def invalidate(self, key: str):
"""失效多级缓存"""
self.local.pop(key, None)
self.redis.delete(key)
# 使用
ml_cache = MultiLevelCache(r)
ml_cache.set("product:1001", {"name": "iPhone", "price": 7999})
product = ml_cache.get("product:1001")
三、消息队列实战
3.1 三种方案对比
#mermaid-svg-fUOaEqK5w0v9mtBB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fUOaEqK5w0v9mtBB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fUOaEqK5w0v9mtBB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fUOaEqK5w0v9mtBB .error-icon{fill:#552222;}#mermaid-svg-fUOaEqK5w0v9mtBB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fUOaEqK5w0v9mtBB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fUOaEqK5w0v9mtBB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fUOaEqK5w0v9mtBB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fUOaEqK5w0v9mtBB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fUOaEqK5w0v9mtBB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fUOaEqK5w0v9mtBB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fUOaEqK5w0v9mtBB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fUOaEqK5w0v9mtBB .marker.cross{stroke:#333333;}#mermaid-svg-fUOaEqK5w0v9mtBB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fUOaEqK5w0v9mtBB p{margin:0;}#mermaid-svg-fUOaEqK5w0v9mtBB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fUOaEqK5w0v9mtBB .cluster-label text{fill:#333;}#mermaid-svg-fUOaEqK5w0v9mtBB .cluster-label span{color:#333;}#mermaid-svg-fUOaEqK5w0v9mtBB .cluster-label span p{background-color:transparent;}#mermaid-svg-fUOaEqK5w0v9mtBB .label text,#mermaid-svg-fUOaEqK5w0v9mtBB span{fill:#333;color:#333;}#mermaid-svg-fUOaEqK5w0v9mtBB .node rect,#mermaid-svg-fUOaEqK5w0v9mtBB .node circle,#mermaid-svg-fUOaEqK5w0v9mtBB .node ellipse,#mermaid-svg-fUOaEqK5w0v9mtBB .node polygon,#mermaid-svg-fUOaEqK5w0v9mtBB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fUOaEqK5w0v9mtBB .rough-node .label text,#mermaid-svg-fUOaEqK5w0v9mtBB .node .label text,#mermaid-svg-fUOaEqK5w0v9mtBB .image-shape .label,#mermaid-svg-fUOaEqK5w0v9mtBB .icon-shape .label{text-anchor:middle;}#mermaid-svg-fUOaEqK5w0v9mtBB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fUOaEqK5w0v9mtBB .rough-node .label,#mermaid-svg-fUOaEqK5w0v9mtBB .node .label,#mermaid-svg-fUOaEqK5w0v9mtBB .image-shape .label,#mermaid-svg-fUOaEqK5w0v9mtBB .icon-shape .label{text-align:center;}#mermaid-svg-fUOaEqK5w0v9mtBB .node.clickable{cursor:pointer;}#mermaid-svg-fUOaEqK5w0v9mtBB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fUOaEqK5w0v9mtBB .arrowheadPath{fill:#333333;}#mermaid-svg-fUOaEqK5w0v9mtBB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fUOaEqK5w0v9mtBB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fUOaEqK5w0v9mtBB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fUOaEqK5w0v9mtBB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fUOaEqK5w0v9mtBB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fUOaEqK5w0v9mtBB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fUOaEqK5w0v9mtBB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fUOaEqK5w0v9mtBB .cluster text{fill:#333;}#mermaid-svg-fUOaEqK5w0v9mtBB .cluster span{color:#333;}#mermaid-svg-fUOaEqK5w0v9mtBB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fUOaEqK5w0v9mtBB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fUOaEqK5w0v9mtBB rect.text{fill:none;stroke-width:0;}#mermaid-svg-fUOaEqK5w0v9mtBB .icon-shape,#mermaid-svg-fUOaEqK5w0v9mtBB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fUOaEqK5w0v9mtBB .icon-shape p,#mermaid-svg-fUOaEqK5w0v9mtBB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fUOaEqK5w0v9mtBB .icon-shape .label rect,#mermaid-svg-fUOaEqK5w0v9mtBB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fUOaEqK5w0v9mtBB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fUOaEqK5w0v9mtBB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fUOaEqK5w0v9mtBB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Redis 消息方案
Pub/Sub
实时广播
List
简单队列
Stream
生产级队列
✅ 实时
❌ 不持久化
❌ 不可靠
✅ 简单
⚠️ 功能有限
❌ 无消费者组
✅ 持久化
✅ 消费者组
✅ 消息确认
✅ 阻塞读取
| 特性 | Pub/Sub | List | Stream |
|---|---|---|---|
| 持久化 | ❌ | ✅ | ✅ |
| 消费者组 | ❌ | ❌ | ✅ |
| 消息确认 | ❌ | ❌ | ✅ |
| 阻塞读取 | ❌ | ✅ | ✅ |
| 消息回溯 | ❌ | ❌ | ✅ |
| 适用场景 | 实时通知 | 简单队列 | 生产级 MQ |
3.2 Stream 详解
#mermaid-svg-Lgfl9DOkiISNvvij{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Lgfl9DOkiISNvvij .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Lgfl9DOkiISNvvij .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Lgfl9DOkiISNvvij .error-icon{fill:#552222;}#mermaid-svg-Lgfl9DOkiISNvvij .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Lgfl9DOkiISNvvij .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Lgfl9DOkiISNvvij .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Lgfl9DOkiISNvvij .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Lgfl9DOkiISNvvij .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Lgfl9DOkiISNvvij .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Lgfl9DOkiISNvvij .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Lgfl9DOkiISNvvij .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Lgfl9DOkiISNvvij .marker.cross{stroke:#333333;}#mermaid-svg-Lgfl9DOkiISNvvij svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Lgfl9DOkiISNvvij p{margin:0;}#mermaid-svg-Lgfl9DOkiISNvvij .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Lgfl9DOkiISNvvij .cluster-label text{fill:#333;}#mermaid-svg-Lgfl9DOkiISNvvij .cluster-label span{color:#333;}#mermaid-svg-Lgfl9DOkiISNvvij .cluster-label span p{background-color:transparent;}#mermaid-svg-Lgfl9DOkiISNvvij .label text,#mermaid-svg-Lgfl9DOkiISNvvij span{fill:#333;color:#333;}#mermaid-svg-Lgfl9DOkiISNvvij .node rect,#mermaid-svg-Lgfl9DOkiISNvvij .node circle,#mermaid-svg-Lgfl9DOkiISNvvij .node ellipse,#mermaid-svg-Lgfl9DOkiISNvvij .node polygon,#mermaid-svg-Lgfl9DOkiISNvvij .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Lgfl9DOkiISNvvij .rough-node .label text,#mermaid-svg-Lgfl9DOkiISNvvij .node .label text,#mermaid-svg-Lgfl9DOkiISNvvij .image-shape .label,#mermaid-svg-Lgfl9DOkiISNvvij .icon-shape .label{text-anchor:middle;}#mermaid-svg-Lgfl9DOkiISNvvij .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Lgfl9DOkiISNvvij .rough-node .label,#mermaid-svg-Lgfl9DOkiISNvvij .node .label,#mermaid-svg-Lgfl9DOkiISNvvij .image-shape .label,#mermaid-svg-Lgfl9DOkiISNvvij .icon-shape .label{text-align:center;}#mermaid-svg-Lgfl9DOkiISNvvij .node.clickable{cursor:pointer;}#mermaid-svg-Lgfl9DOkiISNvvij .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Lgfl9DOkiISNvvij .arrowheadPath{fill:#333333;}#mermaid-svg-Lgfl9DOkiISNvvij .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Lgfl9DOkiISNvvij .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Lgfl9DOkiISNvvij .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lgfl9DOkiISNvvij .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Lgfl9DOkiISNvvij .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lgfl9DOkiISNvvij .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Lgfl9DOkiISNvvij .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Lgfl9DOkiISNvvij .cluster text{fill:#333;}#mermaid-svg-Lgfl9DOkiISNvvij .cluster span{color:#333;}#mermaid-svg-Lgfl9DOkiISNvvij div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Lgfl9DOkiISNvvij .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Lgfl9DOkiISNvvij rect.text{fill:none;stroke-width:0;}#mermaid-svg-Lgfl9DOkiISNvvij .icon-shape,#mermaid-svg-Lgfl9DOkiISNvvij .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lgfl9DOkiISNvvij .icon-shape p,#mermaid-svg-Lgfl9DOkiISNvvij .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Lgfl9DOkiISNvvij .icon-shape .label rect,#mermaid-svg-Lgfl9DOkiISNvvij .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lgfl9DOkiISNvvij .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Lgfl9DOkiISNvvij .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Lgfl9DOkiISNvvij :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} XADD
XREADGROUP
XREADGROUP
XACK
XACK
Producer
Stream
Consumer Group A
Consumer Group B
基础操作
python
"""
Redis Stream 基础操作
"""
# ===== 生产者 =====
def produce_message(stream_name: str, data: dict):
"""发送消息到 Stream"""
message_id = r.xadd(
stream_name,
data,
maxlen=100000, # 最大消息数(裁剪)
approximate=True # 近似裁剪(性能更好)
)
return message_id
# 发送消息
msg_id = produce_message("orders", {
"order_id": "10086",
"user_id": "1001",
"amount": "299.00",
"status": "created"
})
print(f"消息 ID: {msg_id}")
# ===== 消费者组 =====
def create_consumer_group(stream_name: str, group_name: str):
"""创建消费者组"""
try:
r.xgroup_create(stream_name, group_name, id="0", mkstream=True)
except redis.ResponseError as e:
if "BUSYGROUP" in str(e):
pass # 组已存在
else:
raise
def consume_messages(
stream_name: str,
group_name: str,
consumer_name: str,
count: int = 10,
block_ms: int = 5000
):
"""消费消息(阻塞读取)"""
while True:
messages = r.xreadgroup(
groupname=group_name,
consumername=consumer_name,
streams={stream_name: ">"}, # 未读消息
count=count,
block=block_ms
)
if messages:
for stream, stream_messages in messages:
for msg_id, data in stream_messages:
# 处理消息
try:
process_message(data)
# 确认消息
r.xack(stream_name, group_name, msg_id)
except Exception as e:
print(f"处理消息失败: {e}")
# 消息未确认,会被重新投递
else:
print("等待消息...")
def process_message(data: dict):
"""处理消息(业务逻辑)"""
print(f"处理订单: {data}")
# 模拟业务处理
order_id = data.get("order_id")
amount = data.get("amount")
print(f" 订单 {order_id} 金额 {amount} 处理完成")
# 使用
create_consumer_group("orders", "order_processor")
import threading
threading.Thread(
target=consume_messages,
args=("orders", "order_processor", "worker-1"),
daemon=True
).start()
3.3 延迟队列
python
"""
Redis 实现延迟队列(基于 ZSet)
"""
import time
import threading
class RedisDelayQueue:
"""延迟队列(基于 ZSet)"""
def __init__(self, redis_client, queue_name: str = "delay_queue"):
self.redis = redis_client
self.queue_name = queue_name
self.handlers = {}
def publish(self, task_data: str, delay_seconds: int, task_id: str = None):
"""发布延迟任务"""
if task_id is None:
task_id = f"{int(time.time()*1000)}-{id(task_data)}"
# 计算执行时间戳
execute_at = time.time() + delay_seconds
self.redis.zadd(self.queue_name, {task_id: execute_at})
self.redis.hset(f"task:{task_id}", mapping={
"data": task_data,
"execute_at": str(execute_at),
"created_at": str(time.time())
})
return task_id
def consume(self):
"""消费延迟任务(轮询)"""
while True:
now = time.time()
# 查询到期的任务
tasks = self.redis.zrangebyscore(
self.queue_name, 0, now
)
for task_id in tasks:
# 获取任务数据
task_data = self.redis.hget(f"task:{task_id}", "data")
if task_data:
# 从队列移除
self.redis.zrem(self.queue_name, task_id)
# 调用处理函数
handler = self.handlers.get("default")
if handler:
handler(task_data)
# 清理任务数据
self.redis.delete(f"task:{task_id}")
time.sleep(0.5) # 500ms 轮询
def register_handler(self, name: str = "default", handler: callable = None):
"""注册处理函数"""
self.handlers[name] = handler
def start(self):
"""启动消费者"""
thread = threading.Thread(target=self.consume, daemon=True)
thread.start()
return thread
# 使用示例
delay_queue = RedisDelayQueue(r)
# 注册处理函数
delay_queue.register_handler(
handler=lambda data: print(f"执行延迟任务: {data}")
)
# 启动消费者
delay_queue.start()
# 发布延迟任务
delay_queue.publish("30分钟后发送提醒邮件", delay_seconds=1800)
delay_queue.publish("1小时后检查订单状态", delay_seconds=3600)
delay_queue.publish("明天凌晨生成报表", delay_seconds=86400)
print("延迟队列已启动")
四、分布式锁实战
4.1 基础实现
python
"""
Redis 分布式锁基础实现
"""
import uuid
import time
class RedisLock:
"""简单分布式锁(基础版)"""
def __init__(self, redis_client, lock_name: str, ttl: int = 10):
self.redis = redis_client
self.lock_name = f"lock:{lock_name}"
self.ttl = ttl
self.identifier = str(uuid.uuid4())
def acquire(self, timeout: int = 10) -> bool:
"""获取锁"""
end_time = time.time() + timeout
while time.time() < end_time:
# SET key value NX EX
if self.redis.set(self.lock_name, self.identifier, nx=True, ex=self.ttl):
return True
time.sleep(0.1)
return False
def release(self):
"""释放锁(使用 Lua 脚本保证原子性)"""
lua_script = """
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
"""
result = self.redis.eval(lua_script, 1, self.lock_name, self.identifier)
return result == 1
# 使用示例(上下文管理器)
class RedisLockContext:
"""锁上下文管理器"""
def __init__(self, redis_client, lock_name: str, ttl: int = 10, timeout: int = 10):
self.lock = RedisLock(redis_client, lock_name, ttl)
self.timeout = timeout
self.acquired = False
def __enter__(self):
self.acquired = self.lock.acquire(timeout=self.timeout)
if not self.acquired:
raise TimeoutError(f"获取锁超时: {self.lock.lock_name}")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.acquired:
self.lock.release()
# 使用
with RedisLockContext(r, "order:10086", ttl=30):
# 安全地处理订单
process_order(10086)
print("订单处理完成,锁已释放")
4.2 可重入锁
python
"""
可重入分布式锁
"""
import threading
class ReentrantRedisLock:
"""可重入分布式锁"""
def __init__(self, redis_client, lock_name: str, ttl: int = 30):
self.redis = redis_client
self.lock_name = f"rlock:{lock_name}"
self.ttl = ttl
self.identifier = str(uuid.uuid4())
self._local_lock = threading.Lock()
self._lock_count = 0
def acquire(self, timeout: int = 10) -> bool:
"""获取锁(可重入)"""
with self._local_lock:
if self._lock_count > 0:
self._lock_count += 1
return True # 同一线程可重入
end_time = time.time() + timeout
while time.time() < end_time:
# 使用 Hash 存储锁信息(支持重入计数)
lua_script = """
if redis.call("exists", KEYS[1]) == 0 then
redis.call("hset", KEYS[1], "owner", ARGV[1])
redis.call("hset", KEYS[1], "count", 1)
redis.call("pexpire", KEYS[1], ARGV[2])
return 1
elseif redis.call("hget", KEYS[1], "owner") == ARGV[1] then
redis.call("hincrby", KEYS[1], "count", 1)
redis.call("pexpire", KEYS[1], ARGV[2])
return 1
else
return 0
end
"""
result = self.redis.eval(
lua_script, 1,
self.lock_name, self.identifier, self.ttl * 1000
)
if result == 1:
with self._local_lock:
self._lock_count = 1
return True
time.sleep(0.1)
return False
def release(self):
"""释放锁"""
with self._local_lock:
if self._lock_count > 1:
self._lock_count -= 1
return True
lua_script = """
if redis.call("hget", KEYS[1], "owner") == ARGV[1] then
local count = redis.call("hincrby", KEYS[1], "count", -1)
if count == 0 then
return redis.call("del", KEYS[1])
else
return count
end
else
return 0
end
"""
result = self.redis.eval(lua_script, 1, self.lock_name, self.identifier)
return result > 0
4.3 Redisson(推荐生产使用)
python
"""
使用 Redisson 实现分布式锁
(Python 对应库:redis-py + 自行实现,或 redlock-py)
"""
from redlock import RedLock
# 使用 Redlock(红锁算法)
lock = RedLock(
"order:10086",
connection_details=[
{"host": "redis1", "port": 6379},
{"host": "redis2", "port": 6379},
{"host": "redis3", "port": 6379},
],
ttl=10000, # 锁 TTL(毫秒)
retry_delay=200, # 重试间隔(毫秒)
retry_count=3
)
if lock.acquire():
try:
process_order(10086)
finally:
lock.release()
4.4 锁方案对比
#mermaid-svg-MHQEuLPo378AM0GN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MHQEuLPo378AM0GN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MHQEuLPo378AM0GN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MHQEuLPo378AM0GN .error-icon{fill:#552222;}#mermaid-svg-MHQEuLPo378AM0GN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MHQEuLPo378AM0GN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MHQEuLPo378AM0GN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MHQEuLPo378AM0GN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MHQEuLPo378AM0GN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MHQEuLPo378AM0GN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MHQEuLPo378AM0GN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MHQEuLPo378AM0GN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MHQEuLPo378AM0GN .marker.cross{stroke:#333333;}#mermaid-svg-MHQEuLPo378AM0GN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MHQEuLPo378AM0GN p{margin:0;}#mermaid-svg-MHQEuLPo378AM0GN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MHQEuLPo378AM0GN .cluster-label text{fill:#333;}#mermaid-svg-MHQEuLPo378AM0GN .cluster-label span{color:#333;}#mermaid-svg-MHQEuLPo378AM0GN .cluster-label span p{background-color:transparent;}#mermaid-svg-MHQEuLPo378AM0GN .label text,#mermaid-svg-MHQEuLPo378AM0GN span{fill:#333;color:#333;}#mermaid-svg-MHQEuLPo378AM0GN .node rect,#mermaid-svg-MHQEuLPo378AM0GN .node circle,#mermaid-svg-MHQEuLPo378AM0GN .node ellipse,#mermaid-svg-MHQEuLPo378AM0GN .node polygon,#mermaid-svg-MHQEuLPo378AM0GN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MHQEuLPo378AM0GN .rough-node .label text,#mermaid-svg-MHQEuLPo378AM0GN .node .label text,#mermaid-svg-MHQEuLPo378AM0GN .image-shape .label,#mermaid-svg-MHQEuLPo378AM0GN .icon-shape .label{text-anchor:middle;}#mermaid-svg-MHQEuLPo378AM0GN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MHQEuLPo378AM0GN .rough-node .label,#mermaid-svg-MHQEuLPo378AM0GN .node .label,#mermaid-svg-MHQEuLPo378AM0GN .image-shape .label,#mermaid-svg-MHQEuLPo378AM0GN .icon-shape .label{text-align:center;}#mermaid-svg-MHQEuLPo378AM0GN .node.clickable{cursor:pointer;}#mermaid-svg-MHQEuLPo378AM0GN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MHQEuLPo378AM0GN .arrowheadPath{fill:#333333;}#mermaid-svg-MHQEuLPo378AM0GN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MHQEuLPo378AM0GN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MHQEuLPo378AM0GN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MHQEuLPo378AM0GN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MHQEuLPo378AM0GN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MHQEuLPo378AM0GN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MHQEuLPo378AM0GN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MHQEuLPo378AM0GN .cluster text{fill:#333;}#mermaid-svg-MHQEuLPo378AM0GN .cluster span{color:#333;}#mermaid-svg-MHQEuLPo378AM0GN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MHQEuLPo378AM0GN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MHQEuLPo378AM0GN rect.text{fill:none;stroke-width:0;}#mermaid-svg-MHQEuLPo378AM0GN .icon-shape,#mermaid-svg-MHQEuLPo378AM0GN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MHQEuLPo378AM0GN .icon-shape p,#mermaid-svg-MHQEuLPo378AM0GN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MHQEuLPo378AM0GN .icon-shape .label rect,#mermaid-svg-MHQEuLPo378AM0GN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MHQEuLPo378AM0GN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MHQEuLPo378AM0GN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MHQEuLPo378AM0GN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分布式锁方案
基础锁
SET NX EX
可重入锁
Lua + Hash
红锁
Redlock
✅ 简单
⚠️ 不可重入
✅ 可重入
✅ 原子性
✅ 高可用
⚠️ 复杂度高
| 方案 | 可重入 | 高可用 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| SET NX EX | ❌ | ❌ | 低 | 简单场景 |
| Lua + Hash | ✅ | ❌ | 中 | 需要重入 |
| Redlock | ⚠️ | ✅ | 高 | 生产环境 |
五、生产级配置
5.1 内存优化
#mermaid-svg-H733gna0U6OG0wkx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-H733gna0U6OG0wkx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-H733gna0U6OG0wkx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-H733gna0U6OG0wkx .error-icon{fill:#552222;}#mermaid-svg-H733gna0U6OG0wkx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-H733gna0U6OG0wkx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-H733gna0U6OG0wkx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-H733gna0U6OG0wkx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-H733gna0U6OG0wkx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-H733gna0U6OG0wkx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-H733gna0U6OG0wkx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-H733gna0U6OG0wkx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-H733gna0U6OG0wkx .marker.cross{stroke:#333333;}#mermaid-svg-H733gna0U6OG0wkx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-H733gna0U6OG0wkx p{margin:0;}#mermaid-svg-H733gna0U6OG0wkx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-H733gna0U6OG0wkx .cluster-label text{fill:#333;}#mermaid-svg-H733gna0U6OG0wkx .cluster-label span{color:#333;}#mermaid-svg-H733gna0U6OG0wkx .cluster-label span p{background-color:transparent;}#mermaid-svg-H733gna0U6OG0wkx .label text,#mermaid-svg-H733gna0U6OG0wkx span{fill:#333;color:#333;}#mermaid-svg-H733gna0U6OG0wkx .node rect,#mermaid-svg-H733gna0U6OG0wkx .node circle,#mermaid-svg-H733gna0U6OG0wkx .node ellipse,#mermaid-svg-H733gna0U6OG0wkx .node polygon,#mermaid-svg-H733gna0U6OG0wkx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-H733gna0U6OG0wkx .rough-node .label text,#mermaid-svg-H733gna0U6OG0wkx .node .label text,#mermaid-svg-H733gna0U6OG0wkx .image-shape .label,#mermaid-svg-H733gna0U6OG0wkx .icon-shape .label{text-anchor:middle;}#mermaid-svg-H733gna0U6OG0wkx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-H733gna0U6OG0wkx .rough-node .label,#mermaid-svg-H733gna0U6OG0wkx .node .label,#mermaid-svg-H733gna0U6OG0wkx .image-shape .label,#mermaid-svg-H733gna0U6OG0wkx .icon-shape .label{text-align:center;}#mermaid-svg-H733gna0U6OG0wkx .node.clickable{cursor:pointer;}#mermaid-svg-H733gna0U6OG0wkx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-H733gna0U6OG0wkx .arrowheadPath{fill:#333333;}#mermaid-svg-H733gna0U6OG0wkx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-H733gna0U6OG0wkx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-H733gna0U6OG0wkx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-H733gna0U6OG0wkx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-H733gna0U6OG0wkx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-H733gna0U6OG0wkx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-H733gna0U6OG0wkx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-H733gna0U6OG0wkx .cluster text{fill:#333;}#mermaid-svg-H733gna0U6OG0wkx .cluster span{color:#333;}#mermaid-svg-H733gna0U6OG0wkx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-H733gna0U6OG0wkx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-H733gna0U6OG0wkx rect.text{fill:none;stroke-width:0;}#mermaid-svg-H733gna0U6OG0wkx .icon-shape,#mermaid-svg-H733gna0U6OG0wkx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-H733gna0U6OG0wkx .icon-shape p,#mermaid-svg-H733gna0U6OG0wkx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-H733gna0U6OG0wkx .icon-shape .label rect,#mermaid-svg-H733gna0U6OG0wkx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-H733gna0U6OG0wkx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-H733gna0U6OG0wkx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-H733gna0U6OG0wkx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内存优化策略
数据结构优化
过期策略
压缩存储
Hash 代替多个 Key
ZSet 代替 List+Score
合理设置 TTL
TTL 抖动防雪崩
Hash field 压缩
紧凑编码
redis.conf 关键配置:
conf
# 内存限制
maxmemory 4gb
# 淘汰策略
# allkeys-lru: 所有 key,最近最少使用
# volatile-lru: 有过期的 key,最近最少使用
# allkeys-random: 所有 key,随机淘汰
# volatile-ttl: 有过期的 key,即将过期的优先淘汰
maxmemory-policy allkeys-lru
# 内存淘汰采样数(越大越精确,但越慢)
maxmemory-samples 10
# 是否开启 lazyfree
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
淘汰策略选择:
| 策略 | 适用场景 | 说明 |
|---|---|---|
allkeys-lru |
通用推荐 | 淘汰最久未使用的 |
volatile-lru |
需要永久 key 的场景 | 只淘汰有 TTL 的 |
volatile-ttl |
优先淘汰快过期的 | TTL 短的先淘汰 |
allkeys-lfu |
Redis 4.0+ | 淘汰使用频率最低的 |
5.2 持久化方案
#mermaid-svg-iY02xUw4e3nRKKba{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iY02xUw4e3nRKKba .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iY02xUw4e3nRKKba .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iY02xUw4e3nRKKba .error-icon{fill:#552222;}#mermaid-svg-iY02xUw4e3nRKKba .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iY02xUw4e3nRKKba .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iY02xUw4e3nRKKba .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iY02xUw4e3nRKKba .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iY02xUw4e3nRKKba .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iY02xUw4e3nRKKba .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iY02xUw4e3nRKKba .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iY02xUw4e3nRKKba .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iY02xUw4e3nRKKba .marker.cross{stroke:#333333;}#mermaid-svg-iY02xUw4e3nRKKba svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iY02xUw4e3nRKKba p{margin:0;}#mermaid-svg-iY02xUw4e3nRKKba .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iY02xUw4e3nRKKba .cluster-label text{fill:#333;}#mermaid-svg-iY02xUw4e3nRKKba .cluster-label span{color:#333;}#mermaid-svg-iY02xUw4e3nRKKba .cluster-label span p{background-color:transparent;}#mermaid-svg-iY02xUw4e3nRKKba .label text,#mermaid-svg-iY02xUw4e3nRKKba span{fill:#333;color:#333;}#mermaid-svg-iY02xUw4e3nRKKba .node rect,#mermaid-svg-iY02xUw4e3nRKKba .node circle,#mermaid-svg-iY02xUw4e3nRKKba .node ellipse,#mermaid-svg-iY02xUw4e3nRKKba .node polygon,#mermaid-svg-iY02xUw4e3nRKKba .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iY02xUw4e3nRKKba .rough-node .label text,#mermaid-svg-iY02xUw4e3nRKKba .node .label text,#mermaid-svg-iY02xUw4e3nRKKba .image-shape .label,#mermaid-svg-iY02xUw4e3nRKKba .icon-shape .label{text-anchor:middle;}#mermaid-svg-iY02xUw4e3nRKKba .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iY02xUw4e3nRKKba .rough-node .label,#mermaid-svg-iY02xUw4e3nRKKba .node .label,#mermaid-svg-iY02xUw4e3nRKKba .image-shape .label,#mermaid-svg-iY02xUw4e3nRKKba .icon-shape .label{text-align:center;}#mermaid-svg-iY02xUw4e3nRKKba .node.clickable{cursor:pointer;}#mermaid-svg-iY02xUw4e3nRKKba .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iY02xUw4e3nRKKba .arrowheadPath{fill:#333333;}#mermaid-svg-iY02xUw4e3nRKKba .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iY02xUw4e3nRKKba .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iY02xUw4e3nRKKba .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iY02xUw4e3nRKKba .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iY02xUw4e3nRKKba .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iY02xUw4e3nRKKba .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iY02xUw4e3nRKKba .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iY02xUw4e3nRKKba .cluster text{fill:#333;}#mermaid-svg-iY02xUw4e3nRKKba .cluster span{color:#333;}#mermaid-svg-iY02xUw4e3nRKKba div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iY02xUw4e3nRKKba .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iY02xUw4e3nRKKba rect.text{fill:none;stroke-width:0;}#mermaid-svg-iY02xUw4e3nRKKba .icon-shape,#mermaid-svg-iY02xUw4e3nRKKba .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iY02xUw4e3nRKKba .icon-shape p,#mermaid-svg-iY02xUw4e3nRKKba .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iY02xUw4e3nRKKba .icon-shape .label rect,#mermaid-svg-iY02xUw4e3nRKKba .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iY02xUw4e3nRKKba .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iY02xUw4e3nRKKba .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iY02xUw4e3nRKKba :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 持久化
RDB
快照
AOF
日志
✅ 文件紧凑
✅ 恢复快
⚠️ 可能丢失数据
✅ 数据安全
⚠️ 文件大
⚠️ 恢复慢
推荐配置(Redis 7):
conf
# RDB 配置(默认开启)
save 900 1 # 15 分钟内至少 1 次变更
save 300 10 # 5 分钟内至少 10 次变更
save 60 10000 # 1 分钟内至少 10000 次变更
# AOF 配置
appendonly yes
appendfilename "appendonly.aof"
# Redis 7 多部分 AOF
aof-use-rdb-preamble yes # AOF 文件开头用 RDB 格式(加载更快)
# AOF 重写触发条件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# AOF 刷盘策略
# always: 每次写入都刷盘(最安全,最慢)
# everysec: 每秒刷盘(推荐)
# no: 由 OS 决定
appendfsync everysec
| 方案 | 数据安全 | 性能 | 恢复速度 | 适用 |
|---|---|---|---|---|
| RDB | ⚠️ 可能丢数据 | ✅ 高 | ✅ 快 | 备份、容忍少量丢失 |
| AOF | ✅ 最多丢 1 秒 | ⚠️ 中等 | ⚠️ 较慢 | 数据安全要求高 |
| RDB + AOF | ✅ 最佳 | ⚠️ 中等 | ✅ 快 | 生产环境推荐 |
5.3 集群方案
#mermaid-svg-npw75yYwOW6amcxX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-npw75yYwOW6amcxX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-npw75yYwOW6amcxX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-npw75yYwOW6amcxX .error-icon{fill:#552222;}#mermaid-svg-npw75yYwOW6amcxX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-npw75yYwOW6amcxX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-npw75yYwOW6amcxX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-npw75yYwOW6amcxX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-npw75yYwOW6amcxX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-npw75yYwOW6amcxX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-npw75yYwOW6amcxX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-npw75yYwOW6amcxX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-npw75yYwOW6amcxX .marker.cross{stroke:#333333;}#mermaid-svg-npw75yYwOW6amcxX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-npw75yYwOW6amcxX p{margin:0;}#mermaid-svg-npw75yYwOW6amcxX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-npw75yYwOW6amcxX .cluster-label text{fill:#333;}#mermaid-svg-npw75yYwOW6amcxX .cluster-label span{color:#333;}#mermaid-svg-npw75yYwOW6amcxX .cluster-label span p{background-color:transparent;}#mermaid-svg-npw75yYwOW6amcxX .label text,#mermaid-svg-npw75yYwOW6amcxX span{fill:#333;color:#333;}#mermaid-svg-npw75yYwOW6amcxX .node rect,#mermaid-svg-npw75yYwOW6amcxX .node circle,#mermaid-svg-npw75yYwOW6amcxX .node ellipse,#mermaid-svg-npw75yYwOW6amcxX .node polygon,#mermaid-svg-npw75yYwOW6amcxX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-npw75yYwOW6amcxX .rough-node .label text,#mermaid-svg-npw75yYwOW6amcxX .node .label text,#mermaid-svg-npw75yYwOW6amcxX .image-shape .label,#mermaid-svg-npw75yYwOW6amcxX .icon-shape .label{text-anchor:middle;}#mermaid-svg-npw75yYwOW6amcxX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-npw75yYwOW6amcxX .rough-node .label,#mermaid-svg-npw75yYwOW6amcxX .node .label,#mermaid-svg-npw75yYwOW6amcxX .image-shape .label,#mermaid-svg-npw75yYwOW6amcxX .icon-shape .label{text-align:center;}#mermaid-svg-npw75yYwOW6amcxX .node.clickable{cursor:pointer;}#mermaid-svg-npw75yYwOW6amcxX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-npw75yYwOW6amcxX .arrowheadPath{fill:#333333;}#mermaid-svg-npw75yYwOW6amcxX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-npw75yYwOW6amcxX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-npw75yYwOW6amcxX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-npw75yYwOW6amcxX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-npw75yYwOW6amcxX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-npw75yYwOW6amcxX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-npw75yYwOW6amcxX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-npw75yYwOW6amcxX .cluster text{fill:#333;}#mermaid-svg-npw75yYwOW6amcxX .cluster span{color:#333;}#mermaid-svg-npw75yYwOW6amcxX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-npw75yYwOW6amcxX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-npw75yYwOW6amcxX rect.text{fill:none;stroke-width:0;}#mermaid-svg-npw75yYwOW6amcxX .icon-shape,#mermaid-svg-npw75yYwOW6amcxX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-npw75yYwOW6amcxX .icon-shape p,#mermaid-svg-npw75yYwOW6amcxX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-npw75yYwOW6amcxX .icon-shape .label rect,#mermaid-svg-npw75yYwOW6amcxX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-npw75yYwOW6amcxX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-npw75yYwOW6amcxX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-npw75yYwOW6amcxX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 部署方案
单机
主从复制
哨兵
Cluster
开发/测试
读扩展
自动故障转移
数据分片
高可用
大规模
| 方案 | 可用性 | 扩展性 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| 单机 | ❌ 低 | ❌ | 低 | 开发测试 |
| 主从 | ⚠️ 中 | ⚠️ 读扩展 | 低 | 读多写少 |
| Sentinel | ✅ 高 | ⚠️ 读扩展 | 中 | 中小规模 |
| Cluster | ✅ 高 | ✅ 读写扩展 | 高 | 大规模生产 |
六、性能优化
6.1 Pipeline 批量操作
python
"""
Pipeline 批量操作
"""
def batch_get(keys: list) -> list:
"""批量 GET"""
pipe = r.pipeline(transaction=False) # 非事务 Pipeline
for key in keys:
pipe.get(key)
return pipe.execute()
def batch_set(items: dict, ttl: int = 3600):
"""批量 SET"""
pipe = r.pipeline(transaction=False)
for key, value in items.items():
serialized = json.dumps(value, ensure_ascii=False) if not isinstance(value, str) else value
pipe.setex(key, ttl, serialized)
pipe.execute()
# 性能对比
import time
keys = [f"key:{i}" for i in range(10000)]
# 逐个 GET
start = time.time()
for key in keys:
r.get(key)
print(f"逐个 GET: {time.time() - start:.3f}s")
# Pipeline GET
start = time.time()
batch_get(keys)
print(f"Pipeline GET: {time.time() - start:.3f}s")
# Pipeline 通常快 10-50 倍
6.2 BigKey 处理
python
"""
BigKey 检测与处理
"""
def scan_big_keys(redis_client, threshold_bytes=1024*1024):
"""扫描 BigKey(> 1MB)"""
cursor = 0
big_keys = []
while True:
cursor, keys = redis_client.scan(cursor=cursor, count=100)
for key in keys:
key_type = redis_client.type(key)
key_size = redis_client.strlen(key) if key_type == "string" \
else redis_client.hlen(key) if key_type == "hash" \
else redis_client.llen(key) if key_type == "list" \
else redis_client.scard(key) if key_type == "set" \
else redis_client.zcard(key) if key_type == "zset" else 0
if key_size > threshold_bytes:
big_keys.append({
"key": key,
"type": key_type,
"size": key_size
})
if cursor == 0:
break
return big_keys
BigKey 解决方案:
| 场景 | 方案 |
|---|---|
| 大 String | 拆分为多个小 key(Hash) |
| 大 Hash | 使用 HSCAN 分批获取 |
| 大 List | 使用 LRANGE 分页获取 |
| 大 Set | 使用 SSCAN 分批获取 |
七、监控与运维
7.1 关键指标监控
python
"""
Redis 监控指标采集
"""
def get_redis_info(redis_client) -> dict:
"""获取关键监控指标"""
info = redis_client.info()
return {
# 内存
"used_memory_mb": info["used_memory"] / 1024 / 1024,
"used_memory_peak_mb": info["used_memory_peak"] / 1024 / 1024,
"used_memory_rss_mb": info["used_memory_rss"] / 1024 / 1024,
# 连接
"connected_clients": info["connected_clients"],
"blocked_clients": info["blocked_clients"],
# 性能
"ops_per_sec": info.get("instantaneous_ops_per_sec", 0),
"hit_rate": info["keyspace_hits"] / max(info["keyspace_hits"] + info["keyspace_misses"], 1),
# 持久化
"rdb_last_bgsave": info.get("rdb_last_bgsave_status", "ok"),
"aof_enabled": info.get("aof_enabled", 0) == 1,
# 集群
"cluster_enabled": info.get("cluster_enabled", 0) == 1,
}
# 监控告警
def check_redis_health(redis_client):
"""健康检查"""
info = get_redis_info(redis_client)
alerts = []
if info["used_memory_mb"] > info["used_memory_peak_mb"] * 0.8:
alerts.append("⚠️ 内存使用接近峰值")
if info["hit_rate"] < 0.9:
alerts.append(f"⚠️ 缓存命中率过低: {info['hit_rate']:.2%}")
if info["blocked_clients"] > 10:
alerts.append(f"⚠️ 阻塞客户端数: {info['blocked_clients']}")
return alerts
7.2 常用运维命令
| 命令 | 说明 | 使用场景 |
|---|---|---|
INFO memory |
内存信息 | 内存排查 |
INFO stats |
统计信息 | 性能分析 |
INFO replication |
主从信息 | 主从状态 |
SLOWLOG GET 10 |
慢查询日志 | 性能优化 |
CLIENT LIST |
客户端连接 | 连接管理 |
DBSIZE |
key 总数 | 容量监控 |
SCAN |
安全遍历 | 大数据量操作 |
OBJECT ENCODING key |
编码方式 | 内存优化 |
八、最佳实践总结
8.1 开发规范
markdown
✅ Redis 开发规范
📋 Key 命名
□ 使用冒号分隔的命名空间:`project:module:entity:id`
□ 例:`order:detail:10086`、`user:token:1001`
□ 避免过长(< 100 字节)
⏰ TTL 设置
□ 所有 key 都应设置 TTL
□ 热点数据:1-6 小时
□ 冷数据:1-7 天
□ 添加随机抖动(±10%)防雪崩
📦 数据大小
□ 单个 value < 10KB(推荐 < 1KB)
□ 避免 BigKey(> 1MB 严格禁止)
⚡ 性能
□ 批量操作使用 Pipeline
□ 避免使用 KEYS *(用 SCAN)
□ 使用连接池
🔒 安全
□ 生产环境必须设置密码
□ 禁用危险命令(FLUSHALL、CONFIG)
□ 使用 ACL 限制权限
8.2 常见错误
| 错误 | 后果 | 解决方案 |
|---|---|---|
| ❌ 不设 TTL | 内存泄漏 | 所有 key 都设置 TTL |
| ❌ 用 KEYS * | 阻塞 Redis | 使用 SCAN |
| ❌ BigKey | 延迟飙升 | 拆分数据 |
| ❌ 热点 key 单节点 | 单点压力 | 本地缓存 + 分布式 |
| ❌ 大量 Pipeline | 阻塞网络 | 控制批次大小 |
九、总结
#mermaid-svg-uSgsgA0CejxQ1FMH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uSgsgA0CejxQ1FMH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uSgsgA0CejxQ1FMH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uSgsgA0CejxQ1FMH .error-icon{fill:#552222;}#mermaid-svg-uSgsgA0CejxQ1FMH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uSgsgA0CejxQ1FMH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uSgsgA0CejxQ1FMH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uSgsgA0CejxQ1FMH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uSgsgA0CejxQ1FMH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uSgsgA0CejxQ1FMH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uSgsgA0CejxQ1FMH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uSgsgA0CejxQ1FMH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uSgsgA0CejxQ1FMH .marker.cross{stroke:#333333;}#mermaid-svg-uSgsgA0CejxQ1FMH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uSgsgA0CejxQ1FMH p{margin:0;}#mermaid-svg-uSgsgA0CejxQ1FMH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uSgsgA0CejxQ1FMH .cluster-label text{fill:#333;}#mermaid-svg-uSgsgA0CejxQ1FMH .cluster-label span{color:#333;}#mermaid-svg-uSgsgA0CejxQ1FMH .cluster-label span p{background-color:transparent;}#mermaid-svg-uSgsgA0CejxQ1FMH .label text,#mermaid-svg-uSgsgA0CejxQ1FMH span{fill:#333;color:#333;}#mermaid-svg-uSgsgA0CejxQ1FMH .node rect,#mermaid-svg-uSgsgA0CejxQ1FMH .node circle,#mermaid-svg-uSgsgA0CejxQ1FMH .node ellipse,#mermaid-svg-uSgsgA0CejxQ1FMH .node polygon,#mermaid-svg-uSgsgA0CejxQ1FMH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uSgsgA0CejxQ1FMH .rough-node .label text,#mermaid-svg-uSgsgA0CejxQ1FMH .node .label text,#mermaid-svg-uSgsgA0CejxQ1FMH .image-shape .label,#mermaid-svg-uSgsgA0CejxQ1FMH .icon-shape .label{text-anchor:middle;}#mermaid-svg-uSgsgA0CejxQ1FMH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uSgsgA0CejxQ1FMH .rough-node .label,#mermaid-svg-uSgsgA0CejxQ1FMH .node .label,#mermaid-svg-uSgsgA0CejxQ1FMH .image-shape .label,#mermaid-svg-uSgsgA0CejxQ1FMH .icon-shape .label{text-align:center;}#mermaid-svg-uSgsgA0CejxQ1FMH .node.clickable{cursor:pointer;}#mermaid-svg-uSgsgA0CejxQ1FMH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uSgsgA0CejxQ1FMH .arrowheadPath{fill:#333333;}#mermaid-svg-uSgsgA0CejxQ1FMH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uSgsgA0CejxQ1FMH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uSgsgA0CejxQ1FMH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uSgsgA0CejxQ1FMH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uSgsgA0CejxQ1FMH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uSgsgA0CejxQ1FMH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uSgsgA0CejxQ1FMH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uSgsgA0CejxQ1FMH .cluster text{fill:#333;}#mermaid-svg-uSgsgA0CejxQ1FMH .cluster span{color:#333;}#mermaid-svg-uSgsgA0CejxQ1FMH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uSgsgA0CejxQ1FMH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uSgsgA0CejxQ1FMH rect.text{fill:none;stroke-width:0;}#mermaid-svg-uSgsgA0CejxQ1FMH .icon-shape,#mermaid-svg-uSgsgA0CejxQ1FMH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uSgsgA0CejxQ1FMH .icon-shape p,#mermaid-svg-uSgsgA0CejxQ1FMH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uSgsgA0CejxQ1FMH .icon-shape .label rect,#mermaid-svg-uSgsgA0CejxQ1FMH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uSgsgA0CejxQ1FMH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uSgsgA0CejxQ1FMH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uSgsgA0CejxQ1FMH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🎓 Redis 7 实战总结
📦 缓存
📨 消息队列
🔒 分布式锁
穿透:布隆过滤器
击穿:互斥锁
雪崩:随机 TTL
多级缓存
Stream: 生产级
延迟队列: ZSet
基础锁: SET NX
可重入锁: Lua
高可用: Redlock
场景方案选择
| 场景 | 推荐方案 |
|---|---|
| 简单缓存 | String + TTL |
| 对象缓存 | Hash |
| 排行榜 | ZSet |
| 实时通知 | Pub/Sub |
| 可靠消息 | Stream + 消费者组 |
| 延迟任务 | ZSet + 轮询 |
| 简单锁 | SET NX EX |
| 生产锁 | Lua + Hash / Redlock |
本文基于 Redis 7.x 编写,代码示例使用 Python (redis-py)。实际生产环境建议结合业务需求调整参数和方案。如有问题欢迎评论区讨论!