Redis 缓存应用与热点问题实战
1.1 缓存读写流程:Cache Aside(旁路缓存模式)
📌 工作流程:
- 先从缓存查询数据;
- 如果没有命中(cache miss),从数据库加载;
- 将数据写入缓存;
- 返回数据给业务;
- 更新数据时,先更新数据库,再删除缓存。
✅ Python 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import redis import time from functools import wraps
r = redis.Redis(host='localhost', port=6379, db=0)
def cache_aside(key_prefix, ttl=300): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): cache_key = f"{key_prefix}:{args[0]}" result = r.get(cache_key) if result: return result.decode('utf-8') result = func(*args, **kwargs) if result: r.setex(cache_key, ttl, result) return result return wrapper return decorator
@cache_aside("user_info", ttl=60) def get_user_from_db(user_id): print("访问数据库...") time.sleep(1) return f"user_{user_id}_data"
|
1.2 热键(Hot Key)实战分析
🔥 什么是热键?
热键是指访问频率极高的某个 key,常见于以下场景:
- 爆款商品详情页
- 明星账号信息
- 热点新闻推送
- 用户个性化首页
🔍 热键检测方法
方法一:使用 MONITOR(开发环境使用)
输出将展示所有命令,例如:
1 2 3
| "GET" "product:123" "GET" "product:123" "GET" "product:123"
|
可以观察是否有单一 key 高频出现。
方法二:使用 slowlog
检测慢查询
1
| 127.0.0.1:6379> SLOWLOG GET
|
配合监控记录命中频率较高的 key。
方法三:接入 APM 工具(企业推荐)
- SkyWalking
- Pinpoint
- ELK(日志聚合)+ Kibana 分析热点 key 访问日志
🛠️ 企业解决方案
问题 |
解决方式 |
Redis 单点过载 |
本地缓存 + Redis 两级结构(如 Guava + Redis) |
热点 key TTL 同步过期 |
TTL 随机化,避免雪崩 |
热点数据库穿透 |
添加互斥锁,打散请求,加载中给默认值 |
并发缓存穿透 |
异步刷新缓存,使用布隆过滤器防穿透 |
✅ Python 示例:异步缓存刷新 + TTL 打散
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import redis import random import time from threading import Thread
r = redis.Redis()
def get_data_with_hotkey(key): value = r.get(key) if value: return value.decode('utf-8')
if not r.setnx(f"{key}:lock", 1): time.sleep(0.1) return get_data_with_hotkey(key)
value = f"data_from_db_{key}" ttl = 60 + random.randint(0, 30) r.setex(key, ttl, value) r.delete(f"{key}:lock") return value
|
1.3 大键(Big Key)问题分析
🧱 什么是大键?
单个 Redis key 对应的数据量非常大:
- 超长字符串(大 HTML 页面)
- 数万个成员的 Set、ZSet、Hash
- 单 key 占用内存超过 1MB 甚至更多
📈 排查大键的方法
方法一:MEMORY USAGE key
1
| 127.0.0.1:6379> MEMORY USAGE large_key
|
返回该 key 占用的字节数。
方法二:结合 RANDOMKEY、DUMP 和 RESTORE 检测
1 2
| 127.0.0.1:6379> RANDOMKEY 127.0.0.1:6379> DUMP large_key
|
对不确定的 key 进行抽样分析。
方法三:使用脚本或监控工具定期巡检
💡 大键问题的影响
问题 |
描述 |
阻塞主线程 |
Redis 是单线程,大键操作时间长会阻塞 |
哨兵主从切换失败 |
大 key 同步时间过长导致主从失效 |
集群抖动 |
某节点频繁操作大 key 导致响应缓慢 |
🧩 企业级处理建议
场景 |
建议 |
HTML 页面的缓存 |
切片或分页存储为多个 key |
Hash 超大 |
使用嵌套结构存储多个小 Hash,控制字段数 < 5000 |
Set/ZSet |
分片管理,支持分页读取 |
所有大 key |
设置合理的 TTL 防止缓存污染 |
✅ Python 示例:自动检测大键
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import redis
r = redis.Redis()
def scan_big_keys(threshold=1024 * 100): cursor = 0 big_keys = [] while True: cursor, keys = r.scan(cursor, count=100) for key in keys: usage = r.memory_usage(key) if usage and usage > threshold: big_keys.append((key.decode(), usage)) if cursor == 0: break return big_keys
if __name__ == "__main__": results = scan_big_keys() for k, v in results: print(f"大键: {k}, 大小: {v / 1024:.2f} KB")
|
✅ 总结:缓存优化 Checklist