目录
这些只是打开思路,具体怎么使用要根据业务来,不要盲目使用!
内存缓存
适用场景:函数结果缓存,尤其递归或重复计算,例如递归一类计算 ,或者按照某种规则循环取一些配置参数之类,如循环取产品配置的数值表
import time
from flask import Flask
from cachetools import TTLCache, cached
app = Flask(__name__)
# 还有一些别的策略
cache = TTLCache(maxsize=100, ttl=10)
@app.route("/")
def hello_world():
# !!!!函数参数要能hash,因为要作为键
a = temp(str([1]))
return str(a)
@cached(cache)
def temp(x):
a = time.time()
print(a)
return a
分布式缓存
适用场景:多进程/多服务共享缓存,各种锁,计数器
通常使用redis,要是只有key,value且要求不是那么高可考虑Memcached
很常用
数据库查询缓存
ORM层缓存(如Django Cache Framework)
from django.core.cache import cache
def get_articles():
articles = cache.get('all_articles')
if not articles:
articles = Article.objects.all()
cache.set('all_articles', articles, timeout=60*15) # 15分钟
return articles
HTTP响应缓存
请求结果缓存
import time
import requests_cache, requests
from cachetools import TTLCache, cached
from flask import Flask
app = Flask(__name__)
# 本地会生成api_cache.sqlite的数据库文件,存储请求后的数据,到期后重新请求刷新
requests_cache.install_cache(
'api_cache',
backend='sqlite',
expire_after=180 # 3分钟自动过期
)
@app.route("/")
def hello_world():
a = temp(str([1]))
response = requests.get('http://localhost:5000')
print(response.text)
return str()
def temp(x):
a = time.time()
print(a)
return a
if __name__ == "__main__":
app.run(debug=True, port=5001)
ETag缓存(条件请求)
重点记录下,以前没太接触过这种处理方式,但是这种处理方式需要前端配合,对比常规请求返回,可能最大阻力是在和前端沟通上。
核心原理
请求缓存
存储API的完整响应结果(含headers和body)
使用本地存储(如SQLite/文件)或内存缓存
设置合理过期时间(TTL),避免长期不更新
ETag验证
服务器为每个资源生成唯一标识符(ETag)
客户端首次请求保存ETag,后续请求携带If-None-Match头
服务器比对ETag:
- 匹配 → 返回304 Not Modified(空body)
- 不匹配 → 返回完整新数据+新ETag
完整工作流程
sequenceDiagram
participant Client
participant Cache
participant Server
Client->>Cache: 首次请求(无ETag)
Cache->>Server: 转发请求
Server->>Cache: 返回数据 + ETag
Cache->>Client: 返回数据,保存ETag
Client->>Cache: 后续请求(带If-None-Match)
Cache->>Server: 转发请求(含ETag)
alt ETag匹配
Server->>Cache: 304 Not Modified
Cache->>Client: 返回本地缓存
else ETag不匹配
Server->>Cache: 新数据 + 新ETag
Cache->>Client: 返回新数据,更新ETag
end
后端代码示意:
from flask import Flask, request, jsonify
import hashlib
app = Flask(__name__)
def generate_etag(data):
# 根据内容生成ETag(示例使用MD5)
return hashlib.md5(str(data).encode()).hexdigest()
@app.route('/data')
def get_news():
# 实际业务:从数据库获取最新数据
current_data = {"articles": [1]}
# 生成当前ETag
current_etag = generate_etag(current_data)
# 检查客户端ETag
client_etag = request.headers.get('If-None-Match')
if client_etag == current_etag:
return '', 304 # Not Modified
# 返回新数据+ETag
response = jsonify(current_data)
response.headers['ETag'] = current_etag
return response
if __name__ == '__main__':
app.run(debug=True)
浏览器访问示例
文件/资源缓存
本地文件缓存
就是将一些复杂的结果存储到本地文件中,之后读取文件获取结果
import os
import pickle
from hashlib import md5
def cached_file(key, compute_func, cache_dir='.cache'):
os.makedirs(cache_dir, exist_ok=True)
file_hash = md5(key.encode()).hexdigest()
path = os.path.join(cache_dir, file_hash)
if os.path.exists(path):
with open(path, 'rb') as f:
return pickle.load(f)
else:
# !!!这里执行函数,获取数据
data = compute_func()
with open(path, 'wb') as f:
pickle.dump(data, f)
return data
图像处理结果缓存
类似于内存缓存
from PIL import Image
from functools import lru_cache
@lru_cache(maxsize=100)
def load_image(path):
return Image.open(path)
适用场景建议
推荐使用
- 用户动态流(微博/Twitter)
- 新闻/文章列表
- 商品价格(需要及时性但变化不频繁
不推荐使用
- 实时股价/加密货币行情
- 高频更新的聊天消息
- 需要严格实时性的场景
高级优化策略
缓存预热
启动时加载常用数据
防缓存击穿(Mutex Lock)
增加互斥锁,防止重复
from threading import Lock
cache_lock = Lock()
def get_data_safe(key):
data = cache.get(key)
if data is None:
with cache_lock: # 防止并发重复计算
data = compute_data()
cache.set(key, data)
return data
分层缓存(多级缓存)
本地缓存和redis混合使用,很重要要的一个思路
def get_data_multi_level(key):
data = local_cache.get(key)
if not data:
data = redis.get(key)
if not data:
data = db.query(key)
redis.setex(key, 3600, data)
local_cache.set(key, data, 60) # 本地缓存1分钟
return data