本文目录导读:

Python脚本搭配内存与文件缓存是一个常见的性能优化手段,尤其适合计算密集、I/O频繁、或重复读取相同数据的场景。
以下我会从原理、策略、代码实现三个层面进行阐述,并给出可直接运行的Python示例。
核心思路
| 缓存类型 | 存储位置 | 速度 | 生命周期 | 适用场景 |
|---|---|---|---|---|
| 内存缓存 | 进程堆内存(dict、lru_cache) | 纳秒级 | 进程存活期间 | 重复计算函数结果、频繁访问字典/对象 |
| 文件缓存 | 磁盘(pickle、json、sqlite) | 毫秒级 | 可持久化、跨进程 | 大模型向量、API响应、预处理数据 |
最佳实践:
- 优先用内存缓存(LRU)减少函数调用。
- 若数据太大或需跨会话,落盘为文件缓存。
- 组合:内存中放热数据,文件里放冷数据。
内存缓存实现
1 functools.lru_cache —— 自动LRU淘汰
from functools import lru_cache
@lru_cache(maxsize=128) # 最多缓存128个结果
def expensive_function(n):
"""斐波那契、矩阵运算、数据库查询"""
print(f"计算 {n}")
return n ** 2
# 第一次调用会计算
print(expensive_function(10)) # 输出 "计算 10" + 100
# 第二次直接命中缓存
print(expensive_function(10)) # 仅输出 100(无"计算10")
适用:纯函数(无副作用)、递归、重复参数调用。
2 自定义字典缓存 —— 可控失效
class MemoryCache:
def __init__(self, ttl=60):
self.cache = {}
self.ttl = ttl # 过期时间,秒
def get(self, key):
if key in self.cache:
value, timestamp = self.cache[key]
if time.time() - timestamp < self.ttl:
return value
else:
del self.cache[key]
return None
def set(self, key, value):
self.cache[key] = (value, time.time())
适用:需控制缓存时效、手动管理。
文件缓存实现
1 使用 pickle 缓存大型对象
import pickle
import os
def load_with_cache(filename, compute_func, force=False):
"""如果文件存在且未过期,则从文件加载;否则调用 compute_func 并缓存"""
if not force and os.path.exists(filename):
with open(filename, 'rb') as f:
return pickle.load(f)
else:
result = compute_func()
with open(filename, 'wb') as f:
pickle.dump(result, f)
return result
示例:
# 假设 slow_calculation 是大模型推理、网络请求、数据清洗
data = load_with_cache("cache.pkl", lambda: slow_calculation(), force=False)
注意:pickle 不安全,不要加载不可信来源;大对象建议用 joblib 替代(更高效处理Numpy数组)。
2 使用 joblib.Memory —— 智能缓存(推荐)
joblib 专为科学计算设计,自动根据输入参数和代码变化判断是否重算。
from joblib import Memory
# 指定缓存目录
mem = Memory("./cache_dir", verbose=0)
@mem.cache
def compute_expensive(x, y):
print(f"真实计算: x={x}, y={y}")
import time; time.sleep(2)
return x ** y
# 首次调用会执行真实计算
result1 = compute_expensive(2, 10)
# 第二次立即返回(缓存命中)
result2 = compute_expensive(2, 10)
优势:
- 自动识别函数参数。
- 当函数源代码改变时,自动重新计算(哈希校验)。
- 支持大数组分块序列化(
numpy友好)。
组合缓存策略(内存+文件)
典型的双层缓存(L1内存 + L2文件):
import json
import os
import pickle
from functools import lru_cache
class HybridCache:
def __init__(self, file_path="cache.pkl", memory_maxsize=128):
self.file_path = file_path
self.mem_cache = lru_cache(maxsize=memory_maxsize)(self._load_or_compute)
def _load_or_compute(self, key):
"""先查文件,若文件无则计算并存文件"""
# 尝试从文件加载
if os.path.exists(self.file_path):
with open(self.file_path, 'rb') as f:
data = pickle.load(f)
if key in data:
return data[key]
# 没有缓存 -> 计算
value = self._expensive_compute(key)
# 写回文件(简化:每次全量写入)
if os.path.exists(self.file_path):
with open(self.file_path, 'rb') as f:
data = pickle.load(f)
else:
data = {}
data[key] = value
with open(self.file_path, 'wb') as f:
pickle.dump(data, f)
return value
def _expensive_compute(self, key):
"""模拟耗时计算"""
print(f"真实计算 key={key}")
return key ** 2
def get(self, key):
return self.mem_cache(key)
使用方式:
hc = HybridCache("big_cache.pkl", memory_maxsize=100)
print(hc.get(5)) # 第一次:内存未命中->文件未命中->计算->存文件->存内存
print(hc.get(5)) # 第二次:内存命中(极快)
print(hc.get(10)) # 第三次:内存未命中->文件命中(如果已缓存过10)
高级方案与注意事项
1 针对大数据的文件缓存:diskcache
diskcache 是一个专为磁盘缓存优化的库,支持:
- 分段存储:每个key一个文件,避免全量读写。
- 过期时间:TTL自动清理。
- 大容量:支持GB级缓存。
pip install diskcache
from diskcache import Cache
cache = Cache("my_cache_dir")
@cache.memoize(expire=3600) # 缓存1小时
def expensive_function(a, b):
return a ** b
print(expensive_function(2, 10))
2 缓存失效策略
- 时间过期:设置TTL(如
expire)。 - 显式清除:删除文件、
cache.clear()。 - 版本号:函数代码变更时自动失效(
joblib支持)。 - LRU/LFU:内存用
functools.lru_cache,文件用diskcache的LRU策略。
3 并发安全
- 内存缓存:仅在单进程内工作,多进程需加锁或使用共享内存(
multiprocessing.Manager)。 - 文件缓存:避免多进程同时写同一文件,推荐:
- 使用
SQLite作为文件缓存(支持并发读、WAL模式写)。 - 或使用
filelock库加锁。
- 使用
选择路线图
graph TD
A[需要缓存吗?] -->|否| B[直接计算]
A -->|是| C{数据量大小?}
C -->|小于数十MB| D[内存缓存: lru_cache 或 dict]
C -->|数十MB到几GB| E[文件缓存: pickle/joblib/diskcache]
C -->|更大| F[数据库 / Redis / 分布式缓存]
D --> G{需要跨进程/持久化?}
G -->|是| E
G -->|否| D
E --> H{需要自动过期/版本管理?}
H -->|简单| I[pickle + 手动]
H -->|高级| J[joblib.Memory 或 diskcache]
实践建议:
- 简单场景直接用
joblib.Memory(自动管理哈希和IO)。 - 需要细粒度TTL控制用
diskcache。 - 仅内存缓存就用
lru_cache。 - 避免自己造轮子(序列化、并发、过期策略容易出bug)。
这样,你的Python脚本既能享受内存的极速访问,又能利用文件系统实现数据持久化和跨会话复用。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播