简述 Node.js 中的缓存机制

Node.js 在多个层面都内置或支持缓存机制,从模块加载、HTTP 响应到外部数据库结果都可以走缓存路径,以减少 I/O 与重复计算。

来源整理自:vue3js.cn 面试官系列

1. 核心结论

  • Node.js 主要提供三类缓存:模块缓存(require.cache)、内存缓存(开发者自维护的对象/Map)、HTTP 缓存(协议层 Cache-Control/ETag/Last-Modified)。
  • 缓存的目标都是"减少对原始数据源的访问",但失效策略与适用场景各不相同。

2. 背景知识

(1)缓存的基本概念

缓存是一种机制,用于临时存储数据,以减少对原始数据源的访问,从而提高系统性能和响应速度。在 Node.js 中,不同的缓存机制针对不同的应用场景发挥作用。

(2)Node.js 的特点

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,具有单线程、事件驱动、非阻塞 I/O 等特点。这些特点使得缓存机制在 Node.js 中尤为重要,可有效提升性能。

3. 解析

(1)模块缓存

工作原理:Node.js 在首次加载模块时,会将该模块的导出对象存储在 require.cache 对象中。当再次使用 require 函数加载相同模块时,会直接从缓存中获取该模块的导出对象,而不是重新执行模块代码。

作用:避免重复加载和执行模块代码,节省时间和资源,提高模块加载速度。

示例代码:

// module.js
module.exports = { value: 42 };

// main.js
const module1 = require('./module.js');
const module2 = require('./module.js');
console.log(module1 === module2); // 输出: true

(2)内存缓存

工作原理:开发者可以手动在内存中创建对象来存储数据,作为简单的缓存。例如使用普通的 JavaScript 对象,将数据存储在其中,并通过键值对的方式进行读写操作。

作用:对于一些频繁使用且不经常变化的数据,可以存储在内存缓存中,减少对数据库或其他外部资源的访问,提高数据获取速度。

示例代码:

const cache = new Map();
function getData(key) {
  if (cache.has(key)) return cache.get(key);
  // 模拟从数据源获取数据
  const data = `Data for ${key}`;
  cache.set(key, data);
  return data;
}

(3)HTTP 缓存

工作原理:在 Node.js 中使用 HTTP 服务器时,可以利用 HTTP 协议的缓存机制,如 Cache-Control、ETag、Last-Modified 等头部信息。客户端在请求资源时,服务器可以根据这些头部信息判断客户端缓存的资源是否有效,从而决定是否返回新的资源。

  • Expires / Cache-Control:绝对时间/过期时间(强缓存,返回 200 from disk/memory)。
  • Last-Modified / ETag:协商缓存(304 Not Modified)。

作用:减少客户端和服务器之间的数据传输,降低服务器负载,提高用户体验。

示例代码:

const http = require('node:http');
const fs = require('node:fs');
http.createServer((req, res) => {
  fs.readFile('file.txt', (err, data) => {
    if (err) { res.statusCode = 500; return res.end('Error'); }
    res.setHeader('Cache-Control', 'public, max-age=3600');
    res.end(data);
  });
}).listen(3000);

4. 常见误区

(1)忽视模块缓存

误区:每次使用模块时都认为会重新加载和执行模块代码,未意识到模块缓存的存在。

纠正:了解 require.cache 对象,知道 Node.js 会自动缓存已加载的模块,避免不必要的重复加载。

(2)内存缓存管理不当

误区:在使用内存缓存时,不考虑缓存数据的过期和清理,导致内存占用过高。

纠正:为缓存数据设置合理的过期时间,并定期清理过期数据,以避免内存泄漏。

(3)滥用 HTTP 缓存

误区:对所有资源都设置相同的缓存策略,不考虑资源的更新频率和特性。

纠正:根据资源的实际情况,合理设置 Cache-Control、ETag 等头部信息,以达到最佳的缓存效果。