01MVP 标识01MVP
包文档基础设施服务cache 缓存

cache 缓存

高性能缓存管理,支持内存缓存、Next.js 集成和性能监控

概述

@mono/cache 是一个轻量级但功能强大的缓存管理包,提供内存缓存、Next.js 缓存集成和性能监控功能。适用于需要提升应用性能的各种场景。

核心功能

内存缓存

提供简单高效的内存缓存实现,支持 TTL 和自动清理。

基本使用

import { createMemoryCache } from "@mono/cache";

const cache = createMemoryCache();

// 设置缓存(TTL 60 秒)
cache.set("user:123", { name: "Alice" }, 60);

// 获取缓存
const user = cache.get("user:123");

手动管理

import { MemoryCache } from "@mono/cache";

const cache = new MemoryCache();

// 设置多个缓存
cache.set("key1", "value1", 300);
cache.set("key2", "value2", 600);

// 获取统计信息
const stats = cache.getStats();
console.log(`缓存条目数: ${stats.size}`);

// 手动清理过期条目
cache.cleanup();

// 清空所有缓存
cache.clear();

Next.js 缓存集成

简化 Next.js 缓存重新验证操作。

重新验证标签

import { revalidateTags } from "@mono/cache";

// 重新验证单个标签
await revalidateTags(["posts"]);

// 重新验证多个标签
await revalidateTags(["posts", "comments", "users"]);

重新验证路径

import { revalidatePaths } from "@mono/cache";

// 重新验证多个路径
await revalidatePaths(["/blog", "/about"]);

组合重新验证

import { revalidateCache } from "@mono/cache";

// 同时重新验证标签和路径
await revalidateCache({
  tags: ["posts"],
  paths: ["/blog", "/blog/[slug]"],
});

缓存监控

内置性能监控,跟踪缓存命中率。

import { CacheMonitor } from "@mono/cache";

// 记录缓存命中
CacheMonitor.recordHit("user-cache");

// 记录缓存未命中
CacheMonitor.recordMiss("user-cache");

// 获取指标
const metrics = CacheMonitor.getMetrics();
console.log(metrics["user-cache"]);
// { hits: 75, misses: 25, hitRate: 0.75 }

实战示例

示例 1:用户数据缓存

import { createMemoryCache } from "@mono/cache";
import { db } from "@/lib/db";

const userCache = createMemoryCache();

export async function getUser(userId: string) {
  // 尝试从缓存获取
  const cached = userCache.get<User>(`user:${userId}`);
  if (cached) {
    return cached;
  }

  // 从数据库查询
  const user = await db.user.findUnique({
    where: { id: userId },
  });

  if (user) {
    // 缓存 5 分钟
    userCache.set(`user:${userId}`, user, 300);
  }

  return user;
}

export async function updateUser(userId: string, data: UserData) {
  const user = await db.user.update({
    where: { id: userId },
    data,
  });

  // 更新缓存
  userCache.set(`user:${userId}`, user, 300);

  return user;
}

示例 2:API 响应缓存

import { createMemoryCache } from "@mono/cache";

const apiCache = createMemoryCache();

export async function fetchWithCache<T>(
  url: string,
  ttl: number = 300
): Promise<T> {
  // 检查缓存
  const cached = apiCache.get<T>(url);
  if (cached) {
    return cached;
  }

  // 发起请求
  const response = await fetch(url);
  const data = await response.json();

  // 缓存结果
  apiCache.set(url, data, ttl);

  return data;
}

// 使用示例
const posts = await fetchWithCache<Post[]>(
  "https://api.example.com/posts",
  600 // 10 分钟
);

示例 3:Server Action 缓存失效

"use server";

import { revalidateCache } from "@mono/cache";
import { db } from "@/lib/db";

export async function createPost(data: PostData) {
  // 创建文章
  const post = await db.post.create({ data });

  // 重新验证相关缓存
  await revalidateCache({
    tags: ["posts", "blog"],
    paths: ["/blog", `/blog/${post.slug}`],
  });

  return post;
}

export async function deletePost(postId: string) {
  const post = await db.post.delete({
    where: { id: postId },
  });

  // 重新验证缓存
  await revalidateCache({
    tags: ["posts"],
    paths: ["/blog", `/blog/${post.slug}`],
  });

  return post;
}

示例 4:带监控的缓存层

import { createMemoryCache, CacheMonitor } from "@mono/cache";

const cache = createMemoryCache();

export async function getCachedData<T>(
  key: string,
  fetcher: () => Promise<T>,
  ttl: number = 300
): Promise<T> {
  // 尝试从缓存获取
  const cached = cache.get<T>(key);

  if (cached) {
    CacheMonitor.recordHit("data-cache");
    return cached;
  }

  // 缓存未命中
  CacheMonitor.recordMiss("data-cache");

  // 获取数据
  const data = await fetcher();

  // 缓存数据
  cache.set(key, data, ttl);

  return data;
}

// 定期报告缓存性能
setInterval(() => {
  const metrics = CacheMonitor.getMetrics();
  console.log("缓存性能:", metrics);

  // 如果命中率低于 50%,发出警告
  for (const [tag, stats] of Object.entries(metrics)) {
    if (stats.hitRate < 0.5) {
      console.warn(`${tag} 缓存命中率过低: ${stats.hitRate}`);
    }
  }
}, 60000); // 每分钟检查一次

示例 5:自定义缓存管理器

import { CacheManager, createMemoryCache } from "@mono/cache";

class PostCacheManager extends CacheManager {
  private cache = createMemoryCache();

  async clear(postId: string): Promise<void> {
    this.cache.delete(`post:${postId}`);
    this.cache.delete(`post:${postId}:comments`);
  }

  async clearAll(): Promise<void> {
    this.cache.clear();
  }

  async warmup(): Promise<void> {
    // 预热热门文章
    const popularPosts = await db.post.findMany({
      where: { views: { gt: 1000 } },
      take: 100,
    });

    for (const post of popularPosts) {
      this.cache.set(`post:${post.id}`, post, 3600);
    }

    console.log(`预热了 ${popularPosts.length} 篇文章`);
  }

  async getPost(postId: string): Promise<Post | null> {
    const cached = this.cache.get<Post>(`post:${postId}`);
    if (cached) return cached;

    const post = await db.post.findUnique({
      where: { id: postId },
    });

    if (post) {
      this.cache.set(`post:${postId}`, post, 1800);
    }

    return post;
  }
}

// 应用启动时
const postCache = new PostCacheManager();
await postCache.warmup();

示例 6:防止缓存穿透

import { createMemoryCache } from "@mono/cache";

const cache = createMemoryCache();

// 使用 Symbol 表示空值
const NULL_VALUE = Symbol("null");

export async function getWithNullCache<T>(
  key: string,
  fetcher: () => Promise<T | null>,
  ttl: number = 300
): Promise<T | null> {
  // 检查缓存
  const cached = cache.get<T | typeof NULL_VALUE>(key);

  if (cached === NULL_VALUE) {
    return null;
  }

  if (cached) {
    return cached;
  }

  // 获取数据
  const data = await fetcher();

  // 缓存结果(包括 null)
  cache.set(key, data ?? NULL_VALUE, ttl);

  return data;
}

// 使用示例
const user = await getWithNullCache(
  `user:${userId}`,
  () => db.user.findUnique({ where: { id: userId } }),
  300
);

API 参考

MemoryCache 类

构造函数

const cache = new MemoryCache();

方法

  • set<T>(key: string, data: T, ttlSeconds: number): void - 设置缓存
  • get<T>(key: string): T | null - 获取缓存
  • delete(key: string): void - 删除缓存
  • clear(): void - 清空所有缓存
  • cleanup(): void - 清理过期条目
  • getStats(): { size: number; keys: string[] } - 获取统计信息

createMemoryCache 函数

function createMemoryCache(cleanupIntervalMs?: number): MemoryCache

创建带自动清理的缓存实例。

  • cleanupIntervalMs - 清理间隔(毫秒),默认 10 分钟

Next.js 缓存函数

  • revalidateTags(tags: string[]): Promise<void> - 重新验证标签
  • revalidatePaths(paths: string[]): Promise<void> - 重新验证路径
  • revalidateCache(options): Promise<void> - 组合重新验证

CacheManager 抽象类

abstract class CacheManager {
  abstract clear(key: string): void | Promise<void>;
  abstract clearAll(): void | Promise<void>;
  abstract warmup?(): Promise<void>;
}

CacheMonitor 类

静态方法:

  • recordHit(tag: string): void - 记录命中
  • recordMiss(tag: string): void - 记录未命中
  • getMetrics(): Record<string, Stats> - 获取指标
  • reset(): void - 重置指标

缓存策略指南

TTL 选择

数据类型推荐 TTL说明
实时数据30-60 秒在线用户、实时统计
用户数据5-15 分钟用户信息、配置
内容数据30-60 分钟文章、评论
静态配置1-24 小时系统配置、翻译

缓存失效时机

  1. 数据更新时 - 主动删除或更新缓存
  2. 定时失效 - 使用 TTL 自动过期
  3. 标签失效 - Next.js 中使用标签批量失效
  4. 手动失效 - 管理员操作或系统维护

性能优化建议

  1. 缓存预热 - 应用启动时加载常用数据
  2. 分层缓存 - 内存缓存 + Redis + CDN
  3. 监控指标 - 跟踪命中率,优化策略
  4. 避免缓存雪崩 - 使用随机 TTL
  5. 防止缓存穿透 - 缓存空值

最佳实践

  1. 使用类型参数 - 确保类型安全

    const user = cache.get<User>("user:123");
  2. 合理设置 TTL - 根据数据特性选择

    cache.set("hot-data", data, 60);      // 1 分钟
    cache.set("warm-data", data, 600);    // 10 分钟
    cache.set("cold-data", data, 3600);   // 1 小时
  3. 监控缓存性能 - 定期检查命中率

    const metrics = CacheMonitor.getMetrics();
    if (metrics["my-cache"].hitRate < 0.5) {
      // 优化缓存策略
    }
  4. 实现缓存预热 - 提升启动性能

    class MyCacheManager extends CacheManager {
      async warmup() {
        // 加载常用数据
      }
    }
  5. 使用一致的键名 - 便于管理和调试

    // ✅ 推荐
    cache.set(`user:${id}`, user, 300);
    cache.set(`post:${id}:comments`, comments, 600);
    
    // ❌ 不推荐
    cache.set(id, user, 300);
    cache.set("comments", comments, 600);

故障排查

缓存未生效

问题: 数据总是从数据库查询,缓存似乎不工作。

解决方案:

  1. 检查 TTL 是否太短
  2. 确认缓存键名一致
  3. 验证缓存实例是单例
// ❌ 每次创建新实例
function getUser(id: string) {
  const cache = createMemoryCache(); // 错误!
  return cache.get(`user:${id}`);
}

// ✅ 使用全局实例
const cache = createMemoryCache();
function getUser(id: string) {
  return cache.get(`user:${id}`);
}

内存占用过高

问题: 应用内存使用持续增长。

解决方案:

  1. 使用 createMemoryCache 启用自动清理
  2. 缩短 TTL
  3. 定期调用 cleanup()
// 监控缓存大小
setInterval(() => {
  const stats = cache.getStats();
  if (stats.size > 10000) {
    console.warn("缓存条目过多,执行清理");
    cache.cleanup();
  }
}, 60000);

Next.js 缓存未失效

问题: 调用 revalidateTags 后数据仍然是旧的。

解决方案:

  1. 确认标签名称一致
  2. 检查是否在 Server Component 中使用
  3. 验证缓存配置
// ✅ 标签一致
export const revalidate = 3600;
export const tags = ["posts"];

await revalidateTags(["posts"]); // 正确

// ❌ 标签不匹配
export const tags = ["post"];
await revalidateTags(["posts"]); // 不会失效

完整的 API 文档请参考 README.md