本文介绍在存在数据截断(truncate)操作的场景下,为时间序列数据读取方法设计安全、一致的缓存策略,重点解决因缓存未失效导致读取到已删除数据的问题。
在时间序列数据管理中,read(for_date) 方法频繁调用且数据库 I/O 开销显著,引入缓存(如 functools.lru_cache)可大幅提升性能。但若系统同时支持 truncate(cutoff_date) 这类破坏性写操作——它会批量删除指定日期及之后(或包含)的数据——则简单缓存将导致严重一致性问题:缓存中仍保留已被物理删除的日期数据,后续 read(X) 将返回陈旧甚至无效结果。
最直接且鲁棒的解决方案是:将缓存失效逻辑与数据变更操作强绑定。具体而言,不单独维护缓存状态,而是在每次 truncate 执行后,主动清空整个 read 缓存:
from functools import lru_cache, wraps
class DataManager:
def __init__(self):
# 使用实例方法包装,避免静态缓存污染多个实例
self._read_cached = lru_cache(maxsize=128)(self._read_uncached)
def _read_uncached(self, for_date):
# 实际执行 SQL 查询(例如:SELECT * FROM data WHERE date = ?)
return self._execute_sql_query
("SELECT * FROM data WHERE date = ?", (for_date,))
def read(self, for_date):
return self._read_cached(for_date)
def write(self, for_date, data):
self._execute_sql_query("INSERT OR REPLACE INTO data (date, value) VALUES (?, ?)", (for_date, data))
def truncate(self, cutoff_date, inclusive=True):
# 1. 执行实际截断(注意:SQL 中需正确处理 inclusive 逻辑)
op = ">=" if inclusive else ">"
self._execute_sql_query(f"DELETE FROM data WHERE date {op} ?", (cutoff_date,))
# 2. 强制清除所有缓存项 —— 关键一致性保障步骤
self._read_cached.cache_clear()✅ 为什么 cache_clear() 是首选? 简洁可靠:无需追踪哪些日期被删(truncate 可能影响任意范围日期,精确逐项 cache_remove 易遗漏或出错); 成本可控:lru_cache.cache_clear() 是 O(1) 操作,远低于重复查库开销; 语义清晰:truncate 本质是“重置数据边界”,缓存也应同步重置,符合直觉与契约。
⚠️ 注意事项:
综上,在单实例、强一致性优先的场景下,“写操作触发全量缓存清空”是最简、最稳、最易验证的工程实践。