直接使用lru-cache库可快速构建高性能本地LRU缓存,关键在于合理配置max/maxSize(设定容量边界)、精准建模访问行为(如启用updateAgeOnGet、控制noDisposeOnSet)、结合TTL实现时效与空间双重控制,并利用dispose回调安全释放资源。

直接用 lru-cache 库就能搭出高性能、可配置的本地淘汰机制,不用从零写链表和哈希表。关键在合理设置容量边界、访问行为建模和清理钩子,而不是重造轮子。
明确缓存容量与淘汰触发点
LRU 有效性的前提是“空间有限”。必须设定明确的上限,否则不会触发淘汰:
-
按条目数限制:适合键值对大小相对均匀的场景,例如用户会话 ID → 用户基础信息(
max: 1000) -
按内存估算限制:使用
sizeCalculation函数动态评估每个值的近似字节大小,配合maxSize控制总内存占用(如缓存大量 JSON 字符串或 Buffer) -
避免设为 Infinity:即使加了 TTL,不设
max或maxSize就等于没有 LRU 行为——它只会过期,不会因空间不足而主动淘汰
让访问行为真实反映“热度”
LRU 的效果高度依赖“什么算一次有效访问”。默认 get() 和 set() 都会更新访问顺序,但实际业务中需注意:
- 读多写少场景(如配置项缓存),确保每次
get都触发位置更新;不需要可关掉:noDisposeOnSet: true - 写入即热度场景(如实时指标快照),
set必须参与排序,保持默认即可 - 避免误触:批量初始化时用
cache.set(key, value, { noUpdate: true })跳过访问时间更新,防止冷数据被错误置顶
结合 TTL 实现“时效+空间”双重控制
纯 LRU 只管访问顺序,不处理数据是否过期。生产环境建议始终启用 TTL:
- 用
ttl设置全局默认过期时间(如60 * 60 * 1000表示 1 小时) - 个别 key 可覆盖:
cache.set('token:abc', data, { ttl: 5 * 60 * 1000 }) - 开启
updateAgeOnGet: true,让每次读取都刷新剩余 TTL —— 这更符合“活跃即有效”的直觉 - 注意:TTL 到期是惰性清理,真正释放内存依赖下一次
get/set触发的内部扫描,或手动调用prune()
用 dispose 回调做资源善后
被淘汰不只是删数据,更是释放关联资源的机会:
- 缓存的是文件句柄、WebSocket 连接、定时器?在
dispose里显式关闭或清除 - 记录淘汰日志:
console.debug(`Evicted ${key}, size=${value?.length || 0}`),用于后期分析冷热分布 - 避免在
dispose中执行耗时操作(如写磁盘、发网络请求),否则会阻塞后续缓存操作
不复杂但容易忽略:LRU 的威力不在算法本身,而在你如何定义“一条数据该占多少空间”“什么才算一次真实访问”“过期和淘汰谁先谁后”。配对好这三者,本地存储就自然具备自适应收缩能力。


















