用途
内容哈希文件缓存模式用于缓存高成本的文件处理结果,例如 PDF 解析、文本提取、图像分析等。它使用文件内容的 SHA-256 哈希作为缓存键,而不是文件路径。这样做的核心好处是:文件被重命名或移动后缓存依然命中;文件内容一旦改变,缓存自动失效;并且不需要额外的索引文件。
主要能力
- 基于内容哈希的缓存键:对文件内容计算 SHA-256 哈希,分块读取(默认 64KB 一块)以处理大文件,避免一次性将整个文件载入内存。
- 冻结数据类作为缓存条目:使用
@dataclass(frozen=True, slots=True)定义CacheEntry,包含file_hash、source_path和缓存的document结果。 - 基于文件的缓存存储:每个缓存条目以
{hash}.json命名存储,实现 O(1) 查找,无需索引文件。 - 服务层包装(单一职责):处理函数保持纯净,不感知缓存;缓存作为独立的服务层,负责缓存检查、提取和写入。
- 优雅降级:读取缓存时若遇到 JSON 解析错误、值错误或键错误,视为缓存未命中并返回 None,不会崩溃,下次运行会重新处理。
- 惰性目录创建:首次写入时通过
cache_dir.mkdir(parents=True, exist_ok=True)创建缓存目录。
使用方式
核心流程为:计算文件内容哈希 → 读取缓存 → 命中则返回缓存结果 → 未命中则调用纯处理函数提取 → 写入缓存并返回结果。
关键代码结构包括:
compute_file_hash(path):分块计算文件内容的 SHA-256 哈希,文件不存在时抛出FileNotFoundError。CacheEntry:冻结数据类,保存哈希、源路径和提取结果。write_cache(cache_dir, entry)与read_cache(cache_dir, file_hash):以{hash}.json形式读写缓存,读取失败时返回 None。extract_with_cache(file_path, *, cache_enabled=True, cache_dir=Path(".cache")):服务层函数,根据cache_enabled决定是否走缓存,命中/未命中时记录带截断哈希的日志。
设计决策包括:使用 SHA-256 内容哈希实现路径无关与自动失效;采用 {hash}.json 命名实现 O(1) 查找;服务层包装保持提取函数纯净;手动 JSON 序列化以完全控制冻结数据类的序列化;损坏缓存返回 None 实现优雅降级;惰性创建目录。
适用场景
- 文件处理流水线(PDF 解析、OCR、文本提取、图像分析)。
- 需要
--cache/--no-cache选项的 CLI 工具。 - 批处理任务中相同文件在多次运行中重复出现。
- 在不修改现有纯函数的前提下为其添加缓存。
限制与不适用场景
- 必须始终保持最新的数据(如实时数据流)不适合使用。
- 缓存条目可能极其庞大的情况(应考虑流式处理)。
- 结果依赖于文件内容之外的参数时(例如不同的提取配置)不适用。
最佳实践
- 哈希内容而非路径——路径会变,内容身份不变。
- 对大文件分块哈希,避免将整个文件载入内存。
- 保持处理函数纯净,不应感知缓存。
- 记录缓存命中/未命中日志,使用截断哈希便于调试。
- 优雅处理损坏缓存,将无效条目视为未命中,绝不崩溃。
应避免的反模式
- 基于路径的缓存(文件移动/重命名后失效)。
- 在处理函数内部添加缓存逻辑(违反单一职责)。
- 对嵌套冻结数据类使用
dataclasses.asdict()(复杂嵌套类型可能出问题),应改用手动序列化。