如果你是一个 Node.js 开发者,你可能会遇到需要缓存数据的情况。缓存可以显著提高应用的性能,减少对数据库或其他外部资源的频繁访问。在众多缓存解决方案中,lru-cache 是一个非常受欢迎且易于使用的包。今天,我将带你了解一下 lru-cache 及其基本用法。
1 什么是LRU算法
LRU 是 Least Recently Used 的缩写,意思是“最近最少使用”。LRU 缓存是一种缓存策略,它会自动淘汰最近最少使用的缓存项。这样可以确保缓存中的数据是最常访问的,从而提高缓存的命中率。
2 基本用法
在开始使用 lru-cache 之前,你需要先安装它。你可以使用 npm 或 yarn 来安装:
npm install lru-cache
安装完成后,我们可以开始使用 lru-cache 了。下面是一个简单的示例:
选项解释
在上面的示例中,我们使用了 max 和 maxAge 两个选项来配置缓存。下面是一些常用的选项:
max: 缓存的最大条目数。当缓存超过这个数量时,最老的条目会被自动删除。
maxSize: 缓存的最大大小。maxSize的限制会优先于max,有可能会导致max小于预定义的值。设置maxSize时,每个项目都必须提供大小,要么通过提供给构造函数的sizeCalculation方法,要么通过提供给cache.set()的大小或sizeCalculation选项。每个项目的大小必须是正整数。
maxAge: 每个条目的最大存活时间(毫秒)。超过这个时间,条目会被自动删除。
length: 一个函数,用于计算每个条目的长度。默认情况下,每个条目的长度为 1。
dispose: 一个函数,当条目被删除时会调用这个函数。可以用于清理资源。
stale: 如果设置为 true,当条目过期时仍然可以返回它,但会在后台异步删除。
3 高级用法
除了基本的缓存操作,lru-cache 还提供了一些高级功能。例如,你可以使用 peek 方法来查看条目但不更新其最近使用时间:
cache.set('key3', 'value3');
console.log(cache.peek('key3')); // 输出: 'value3'
你还可以使用 keys 和 values 方法来获取缓存中所有键和值的数组:
使用fetchMethod可以在缓存项不存在时自动获取数据,并将其缓存
使用dispose和disposeAfter选项,可以在缓存项被移除时执行自定义逻辑,例如做一些链接的关闭之类的。
4 核心逻辑
github仓库:github.com/isaacs/node…
里面有很多#号,这个代表是私有变量或者私有函数。
在设计上,其把value和key分别存储在两个数组里,以及Map里,用于快速查找
- keyList
- valueList
把key和value分别存储,可以做一个隔离,避免其它操作能修改到key的值。核心的数据结构为Map和链表,
-
Map(keyMap):用于存储缓存项,提供 O(1) 的查找和插入操作。
-
双向链表(prev和next):用于维护缓存项的使用顺序,支持高效的节点移动和删除操作。
核心函数:
set: 添加缓存,会先做一些异常判断,再看是否存在,不存在则新增并更新队列,存在则替换,并处理一些回调
has:判断是否存在,并更新时间,不会挪到队尾
get:获取缓存,存在的时候会挪到队尾并更新时间
moveToTail:把元素挪到队尾
核心处理逻辑:
-
每次新增或者设置,都会确保元素挪到队尾;
-
每次访问后也会把元素挪到队尾;
-
移除元素,直接从map里获取对应的index,实现了O(n);
-
当要淘汰历史元素的时候,就需要把队首删除;
个人觉得比较有意思的是对请求的处理
-
pcall的参数有点迷人,实际为resolve和reject
-
通过判断是否Promise来
-
决定是否调用then
-
使用AbortController来对请求做控制,如果已经存在就先abort再发起,确保了不同时发起多个请求
-
对AbortController做了补丁,不存在就用自己实现的,自己去实现则使用了回调
很简单的实现,还贴心的打印了warn,真是一个好习惯。
总结
lru-cache 是一个强大且易于使用的缓存包,适用于各种 Node.js 应用。通过合理地使用缓存,你可以显著提高应用的性能,减少对外部资源的依赖。如果你还没有尝试过 lru-cache,不妨在你的下一个项目中试试吧!
你觉得 lru-cache 怎么样?有没有其他你喜欢的缓存策略或工具?欢迎分享你的看法!