高效缓存策略:带你深入了解Node.js的lru-cache模块!

766 阅读5分钟

Foxmail20240618071554.png

如果你是一个 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 怎么样?有没有其他你喜欢的缓存策略或工具?欢迎分享你的看法!