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 小时 | 系统配置、翻译 |
缓存失效时机
- 数据更新时 - 主动删除或更新缓存
- 定时失效 - 使用 TTL 自动过期
- 标签失效 - Next.js 中使用标签批量失效
- 手动失效 - 管理员操作或系统维护
性能优化建议
- 缓存预热 - 应用启动时加载常用数据
- 分层缓存 - 内存缓存 + Redis + CDN
- 监控指标 - 跟踪命中率,优化策略
- 避免缓存雪崩 - 使用随机 TTL
- 防止缓存穿透 - 缓存空值
最佳实践
-
使用类型参数 - 确保类型安全
const user = cache.get<User>("user:123"); -
合理设置 TTL - 根据数据特性选择
cache.set("hot-data", data, 60); // 1 分钟 cache.set("warm-data", data, 600); // 10 分钟 cache.set("cold-data", data, 3600); // 1 小时 -
监控缓存性能 - 定期检查命中率
const metrics = CacheMonitor.getMetrics(); if (metrics["my-cache"].hitRate < 0.5) { // 优化缓存策略 } -
实现缓存预热 - 提升启动性能
class MyCacheManager extends CacheManager { async warmup() { // 加载常用数据 } } -
使用一致的键名 - 便于管理和调试
// ✅ 推荐 cache.set(`user:${id}`, user, 300); cache.set(`post:${id}:comments`, comments, 600); // ❌ 不推荐 cache.set(id, user, 300); cache.set("comments", comments, 600);
故障排查
缓存未生效
问题: 数据总是从数据库查询,缓存似乎不工作。
解决方案:
- 检查 TTL 是否太短
- 确认缓存键名一致
- 验证缓存实例是单例
// ❌ 每次创建新实例
function getUser(id: string) {
const cache = createMemoryCache(); // 错误!
return cache.get(`user:${id}`);
}
// ✅ 使用全局实例
const cache = createMemoryCache();
function getUser(id: string) {
return cache.get(`user:${id}`);
}内存占用过高
问题: 应用内存使用持续增长。
解决方案:
- 使用
createMemoryCache启用自动清理 - 缩短 TTL
- 定期调用
cleanup()
// 监控缓存大小
setInterval(() => {
const stats = cache.getStats();
if (stats.size > 10000) {
console.warn("缓存条目过多,执行清理");
cache.cleanup();
}
}, 60000);Next.js 缓存未失效
问题: 调用 revalidateTags 后数据仍然是旧的。
解决方案:
- 确认标签名称一致
- 检查是否在 Server Component 中使用
- 验证缓存配置
// ✅ 标签一致
export const revalidate = 3600;
export const tags = ["posts"];
await revalidateTags(["posts"]); // 正确
// ❌ 标签不匹配
export const tags = ["post"];
await revalidateTags(["posts"]); // 不会失效完整的 API 文档请参考 README.md。