您现在的位置是:网站首页 > 缓存策略文章详情

缓存策略

缓存策略的基本概念

缓存策略是提升应用性能的关键手段之一,通过减少重复计算或数据请求来优化响应速度。在Node.js中,缓存可以应用于多个层面,包括内存缓存、分布式缓存以及HTTP缓存等。不同的场景需要不同的策略,合理选择能显著降低服务器负载并提高用户体验。

内存缓存

内存缓存是最简单的缓存形式,数据直接存储在进程内存中。Node.js的Map或普通对象常被用作内存缓存容器。例如:

const cache = new Map();

function getData(key) {
  if (cache.has(key)) {
    return cache.get(key);
  }
  const data = fetchDataFromDB(key); // 模拟耗时操作
  cache.set(key, data);
  return data;
}

这种方式的优点是速度快,但缺点也很明显:进程重启后缓存会丢失,且不适合多实例部署的场景。

缓存过期与淘汰策略

时间过期(TTL)

通过设置缓存项的存活时间(Time To Live)自动失效:

const cache = new Map();

function setWithTTL(key, value, ttl) {
  cache.set(key, {
    value,
    expire: Date.now() + ttl
  });
}

function getWithTTL(key) {
  const item = cache.get(key);
  if (!item || item.expire < Date.now()) {
    cache.delete(key);
    return null;
  }
  return item.value;
}

LRU算法

当缓存空间不足时,Least Recently Used算法会优先淘汰最久未使用的数据:

class LRUCache {
  constructor(maxSize) {
    this.maxSize = maxSize;
    this.cache = new Map();
  }

  get(key) {
    if (!this.cache.has(key)) return null;
    const value = this.cache.get(key);
    this.cache.delete(key);
    this.cache.set(key, value);
    return value;
  }

  set(key, value) {
    if (this.cache.has(key)) {
      this.cache.delete(key);
    } else if (this.cache.size >= this.maxSize) {
      this.cache.delete(this.cache.keys().next().value);
    }
    this.cache.set(key, value);
  }
}

分布式缓存

对于多实例部署的场景,需要使用Redis或Memcached等分布式缓存方案:

const redis = require('redis');
const client = redis.createClient();

async function getProduct(id) {
  const cacheKey = `product:${id}`;
  const cachedData = await client.get(cacheKey);
  if (cachedData) return JSON.parse(cachedData);

  const dbData = await fetchFromDatabase(id);
  await client.setEx(cacheKey, 3600, JSON.stringify(dbData)); // 1小时过期
  return dbData;
}

HTTP缓存策略

强制缓存

通过Cache-Control头实现:

app.get('/static/image.jpg', (req, res) => {
  res.setHeader('Cache-Control', 'public, max-age=31536000');
  res.sendFile('image.jpg');
});

协商缓存

使用ETag或Last-Modified:

app.get('/data', async (req, res) => {
  const data = await fetchData();
  const etag = generateETag(data);
  
  if (req.headers['if-none-match'] === etag) {
    return res.status(304).end();
  }
  
  res.setHeader('ETag', etag);
  res.json(data);
});

缓存穿透与雪崩防护

布隆过滤器

防止缓存穿透的典型方案:

const { BloomFilter } = require('bloom-filters');
const filter = new BloomFilter(1000, 0.01);

// 预热合法键
validKeys.forEach(key => filter.add(key));

function getData(key) {
  if (!filter.has(key)) return null; // 快速拦截非法请求
  // ...正常缓存逻辑
}

缓存预热与错峰过期

防止雪崩的解决方案:

// 启动时预热
async function warmUpCache() {
  const hotData = await getHotDataFromDB();
  await Promise.all(
    hotData.map(item => 
      client.setEx(`item:${item.id}`, 
        Math.floor(3600 + Math.random() * 600), // 随机过期时间
        JSON.stringify(item))
    )
  );
}

多级缓存架构

大型系统常采用多级缓存策略:

async function getDataWithMultiCache(id) {
  // 1. 检查内存缓存
  const memCached = localCache.get(id);
  if (memCached) return memCached;

  // 2. 检查Redis缓存
  const redisCached = await redisClient.get(`data:${id}`);
  if (redisCached) {
    localCache.set(id, redisCached); // 回填内存缓存
    return redisCached;
  }

  // 3. 查询数据库
  const dbData = await db.query('SELECT * FROM data WHERE id = ?', [id]);
  
  // 异步更新缓存
  redisClient.setEx(`data:${id}`, 300, JSON.stringify(dbData));
  localCache.set(id, dbData);
  
  return dbData;
}

缓存一致性处理

双删策略

async function updateData(id, newData) {
  // 先删缓存
  await redisClient.del(`data:${id}`);
  
  // 更新数据库
  await db.update('data', newData, { where: { id } });
  
  // 延迟后再删一次
  setTimeout(async () => {
    await redisClient.del(`data:${id}`);
  }, 1000);
}

发布订阅模式

// 更新数据时发布消息
redisClient.publish('cache-invalidate', `data:${id}`);

// 其他服务订阅
redisClient.subscribe('cache-invalidate', (channel, key) => {
  localCache.delete(key.split(':')[1]);
});

性能监控与调优

缓存命中率监控示例:

const cacheStats = {
  hits: 0,
  misses: 0
};

function getDataWithStats(key) {
  if (cache.has(key)) {
    cacheStats.hits++;
    return cache.get(key);
  }
  cacheStats.misses++;
  // ...正常获取逻辑
}

// 定时上报指标
setInterval(() => {
  const hitRate = cacheStats.hits / (cacheStats.hits + cacheStats.misses);
  metrics.report('cache.hit_rate', hitRate);
}, 60000);

上一篇: 垃圾回收机制

下一篇: 负载测试

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步