在高并发系统中,如果没有合适的缓存防护机制,数据库就像裸奔一样,很容易在某个节点集体崩溃。特别是像“缓存穿透”、“缓存击穿”、“缓存雪崩”这三兄弟,稍微一个不注意,就能让系统压力直线上升。本篇文章结合 Flask + Redis 的接口 Demo,聊聊实际项目中我们怎么构建防护机制,保证系统能稳稳当当抗住各种冲击。
想象一个场景:你的系统平时访问量不大,但突然一波高并发请求打进来,某个热点数据刚好缓存过期,然后所有请求一起冲向数据库……结果数据库直接罢工,后端服务连带挂掉,用户报错满屏飞。
这就是经典的“缓存击穿”现场。
再比如说,如果有人频繁请求根本不存在的数据项,如果你没有做“空值缓存”,每次都得去数据库查一遍,很容易撑爆数据库——这叫“缓存穿透”。
更可怕的是,当缓存大面积过期,或者 Redis 节点故障,缓存瞬间全失效,全量请求打向数据库,那就是“缓存雪崩”。
听起来挺吓人?别急,下面我们拆开讲讲这三个问题怎么一一解决。
本质: 请求数据根本就不存在,缓存也不存,数据库每次都查,白查一通。
应对策略: 缓存空值。
本质: 单个高频 Key 到期后,大量请求并发击向数据库。
应对策略: 设置互斥锁、热点隔离、二级缓存。
本质: 大量 Key 同时失效,瞬时流量击穿数据库。
应对策略: 过期时间随机化、预加载、异步刷新等。
我们来构建一个接口:查询商品详情。假设商品信息来自数据库,但我们希望通过 Redis 缓存提升性能并添加防护。
pip install flask redis
.
├── app.py # Flask 主应用
├── cache_utils.py # 缓存封装逻辑
├── mock_db.py # 模拟数据库查询
└── README.md
mock_db.py
:模拟数据库查询import time
MOCK_DB = {
"1001": {"id": "1001", "name": "iPhone", "stock": 10},
"1002": {"id": "1002", "name": "MacBook", "stock": 5},
}
def query_product_from_db(product_id):
time.sleep(0.5) # 模拟延迟
return MOCK_DB.get(product_id)
cache_utils.py
:封装缓存读取逻辑(含空值缓存、击穿保护)import redis
import json
import time
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
EMPTY_CACHE = "__null__"
def get_cache_with_fallback(key, loader, ttl=60, empty_ttl=30):
data = r.get(key)
if data:
if data == EMPTY_CACHE:
return None
return json.loads(data)
# 防击穿:加一个简单互斥锁
lock_key = f"{key}_lock"
if r.setnx(lock_key, "1"):
r.expire(lock_key, 5)
try:
result = loader()
if result:
r.set(key, json.dumps(result), ex=ttl)
else:
r.set(key, EMPTY_CACHE, ex=empty_ttl)
return result
finally:
r.delete(lock_key)
else:
time.sleep(0.05)
return get_cache_with_fallback(key, loader, ttl, empty_ttl)
app.py
:商品详情接口from flask import Flask, jsonify
from cache_utils import get_cache_with_fallback
from mock_db import query_product_from_db
app = Flask(__name__)
@app.route("/product/<product_id>")
def get_product(product_id):
def db_loader():
return query_product_from_db(product_id)
cache_key = f"product:{product_id}"
product = get_cache_with_fallback(cache_key, db_loader, ttl=60, empty_ttl=20)
if product:
return jsonify({"code": 0, "data": product})
else:
return jsonify({"code": 404, "msg": "Product not found"})
if __name__ == "__main__":
app.run(debug=True)
热点商品详情如果不做击穿保护,活动一开始,所有请求会集中打向数据库。通过二级缓存 + 锁机制,可以有效兜住第一波流量。
用户 ID 错误或恶意刷接口时,如果不做空值缓存,容易把数据库搞死。设置空值缓存,是一种非常实用的“打假”手段。
推荐数据一般有个统一刷新周期,一旦批量过期,可以通过设置随机过期时间 + 缓存预热来缓解。
会,但设置较短的 empty_ttl
,比如 30 秒,可以在防止攻击的同时兼顾空间利用。
性能肯定有影响,但锁是保护机制,牺牲的是少部分性能,换来整体系统稳定。
不能 100% 防住,但通过随机过期、预热加载、热点隔离等多种手段组合,大部分场景都能“救回来”。
缓存是把双刃剑,用好了可以大幅提升性能,用不好就是一场灾难。我们要认识到:
架构不是一开始就搭好塔楼,而是一步一步“围墙叠砖”式演进。
未来系统如果规模继续扩大,我们还可以引入以下策略:
这些都属于“更进一步”的优化方案,但第一步,先把基础的保护措施做好,系统才能撑得住下一波高并发浪潮。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。