免责声明:作为非英语母语者,我使用了 AI 来帮助组织和润色本文。
几天前,我分享了 在 Node.js 中实现零分配 S3-FIFO 缓存——一个探索性的 v0.1 版本,展示了如何使用预分配的 TypedArray 实现 S3-FIFO(简单且可扩展的扫描抵抗 FIFO) 缓存算法,以消除 Node.js 中的垃圾回收(GC)压力。
今天,经过广泛的压力测试、架构强化以及 100% 的单元测试覆盖率,我很高兴地宣布 s3fifo v1.0.0 正式发布!🚀
快速回顾:为什么选择 S3-FIFO + 零分配?
传统的 LRU(最近最少使用) 缓存存在两个主要的实际生产缺陷:
- 单次命中污染缓存:扫描或顺序查询会用仅访问一次的项淹没缓存,逐出您的高频工作集。
-
垃圾回收开销:基于对象的朴素缓存实现会在每次
set()时动态分配节点,在高吞吐量工作负载下触发频繁的 GC 暂停。 S3-FIFO 通过将缓存条目组织成三个轻量级队列来解决缓存污染问题: - 小队列 (S):快速过滤掉单次命中(通常占总容量的 ~10%)。
- 主队列 (M):保存多次访问的高频项目(约占 90% 的容量)。
-
幽灵队列 (G):记住从小队列中逐出的键,以便在重新请求时立即提升到主队列。
s3fifo通过使用连续的 TypedArray(Uint32Array、Float64Array)和可重用索引池来支持这些队列,实现了 热get/set周期中的零动态对象分配。 --- ## v1.0.0 的新功能?(生产就绪) 虽然 v0.1 专注于核心算法速度,但 v1.0.0 提供了任务关键型 Node.js 微服务所需的所有开发者人体工程学、生命周期工具和安全保证。 ### 1. 💾 冷启动持久性 (dump&load) 防止容器重启或部署期间的 数据库惊群效应 / 缓存雪崩。s3fifo1.0 支持序列化活动驻留条目及其原始创建时间戳和剩余 TTL:
import fs from "node:fs";
import { S3Fifo } from "s3fifo";
const cache = new S3Fifo<string>({ max: 10000 });
// 1. 导出活动缓存项(可选择过滤掉临时键)
const dumpData = cache.dump((key, value) => !key.startsWith("temp:"));
fs.writeFileSync("cache-snapshot.json", JSON.stringify(dumpData));
// 2. 在服务器启动/预热时:立即恢复缓存状态
const snapshot = JSON.parse(fs.readFileSync("cache-snapshot.json", "utf-8"));
cache.load(snapshot);
进入全屏模式 退出全屏模式
2. ♻️ 安全资源生命周期 (dispose 回调)
当缓存项被逐出、覆盖或清除时,您通常需要释放外部资源(例如,关闭文件描述符、销毁数据库句柄或跟踪逐出指标)。
const cache = new S3Fifo<Buffer>({
max: 500,
dispose: (key, buffer, reason) => {
console.log(`Key ${key} removed due to: ${reason}`); // 'evict' | 'set' | 'delete' | 'clear'
// 安全地释放本地内存或资源
},
});
进入全屏模式 退出全屏模式
🛡️ 重入保护:在缓存操作完成之前,
dispose回调被安全地延迟,从而防止如果回调递归调用cache.set()或cache.delete()导致的内部状态损坏。3. 🔍 无副作用检查 (
peek)需要检查缓存值以进行日志记录、健康检查或监控,而不增加频率计数器或更改逐出状态?
peek()允许纯净、无副作用的读取:
// 不修改 S3-FIFO 频率位字段或 TTL 时间戳
const val = cache.peek("user:1001");
进入全屏模式 退出全屏模式
4. 🔄 标准 ES6 迭代器和 Map API
s3fifo 1.0 与 JavaScript 的原生迭代协议无缝集成:
// 标准 JS Map 风格迭代器
for (const [key, value] of cache) {
console.log(key, value);
}
const keys = Array.from(cache.keys());
const values = Array.from(cache.values());
const entries = Array.from(cache.entries());
cache.forEach((val, key) => {
/* ... */
});
进入全屏模式 退出全屏模式
5. 🧹 无泄漏的生命周期拆除 (close)
在无服务器、热重载 (HMR) 或测试环境中,后台计时器可能会阻止进程终止。close() 方法会清除活动的后台 TTL 计时器,释放内存缓冲区,并防止内存泄漏:
// 关闭时进行清理
cache.close();
console.log(cache.isClosed); // true
进入全屏模式 退出全屏模式
📊 基准测试:命中率和吞吐量 vs lru-cache
在 Zipfian 分布(偏度 0.99,工作集为 100,000 个键)上测试,比较了 s3fifo v1.0 和 Node 流行的 lru-cache:
平均命中率 (%)
| 缓存大小(占池的百分比) | lru-cache | s3fifo |
|---|---|---|
| 1% | 48.90% | 58.30% |
| 5% | 65.00% | 71.10% |
| 10% | 72.30% | 76.40% |
| 25% | 82.10% | 82.70% |
| 50% | 89.00% | 86.30% |
平均吞吐量 (ops/sec)
| 缓存大小(占池的百分比) | lru-cache | s3fifo |
|---|---|---|
| 1% | 10.8M | 15.5M |
| 5% | 10.8M | 14.4M |
| 10% | 10.2M | 14.3M |
| 25% | 10.3M | 13.5M |
| 50% | 10.3M | 14.7M |
关键要点:
- 命中率最高可提高 +9.4%,当缓存容量相对于数据集大小较小时(1%–10% 范围)——非常适合前端数据库缓存或高并发微服务。
- ~40% 更高的吞吐量(~14.5M–15.5M ops/sec vs ~10.5M ops/sec),因为在 get/set 操作期间实现了零对象分配。
开始使用
通过 npm 安装:
npm install s3fifo
进入全屏模式 退出全屏模式
基本用法:
import { S3Fifo } from "s3fifo";
const cache = new S3Fifo<string>({
max: 1000,
ttl: 60000, // 60s 全局 TTL
});
cache.set("session:abc", "user_data");
console.log(cache.get("session:abc")); // 'user_data'
console.log(cache.size); // 1
进入全屏模式 退出全屏模式
链接和反馈
- 📦 NPM:s3fifo
- 🐙 GitHub 仓库:BJS-kr/s3fifo
如果您正在寻找 Node.js 中具有扫描抵抗和最小 GC 占用的内存缓存替代方案,请尝试
s3fifo!非常感谢 Bug 报告、反馈和 GitHub 星标!🙏
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.