名称: redis-patterns
描述: Upstash Redis 缓存和限流模式。
Upstash Redis 模式
设置
// lib/redis.ts
import { Redis } from '@upstash/redis';
export const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
基础缓存
// 带TTL的缓存
async function getCachedUser(id: string): Promise<User | null> {
const cacheKey = `user:${id}`;
// 先尝试从缓存读取
const cached = await redis.get<User>(cacheKey);
if (cached) return cached;
// 从数据库获取
const user = await db.query.users.findFirst({
where: eq(users.id, id),
});
if (user) {
// 缓存5分钟
await redis.setex(cacheKey, 300, user);
}
return user;
}
缓存失效
// 更新时失效缓存
async function updateUser(id: string, data: UpdateUserInput): Promise<User> {
const user = await db.update(users)
.set(data)
.where(eq(users.id, id))
.returning();
// 失效缓存
await redis.del(`user:${id}`);
// 同时失效列表缓存
await redis.del('users:list');
return user[0];
}
速率限制
import { Ratelimit } from '@upstash/ratelimit';
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, '10 s'), // 10秒内10次请求
analytics: true,
});
// 在API路由或中间件中
export async function POST(request: Request) {
const ip = request.headers.get('x-forwarded-for') ?? 'anonymous';
const { success, limit, reset, remaining } = await ratelimit.limit(ip);
if (!success) {
return new Response('请求过多', {
status: 429,
headers: {
'X-RateLimit-Limit': limit.toString(),
'X-RateLimit-Remaining': remaining.toString(),
'X-RateLimit-Reset': reset.toString(),
},
});
}
// 处理请求...
}
会话存储
interface Session {
userId: string;
expiresAt: number;
}
async function createSession(userId: string): Promise<string> {
const sessionId = crypto.randomUUID();
const session: Session = {
userId,
expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7天
};
await redis.setex(`session:${sessionId}`, 7 * 24 * 60 * 60, session);
return sessionId;
}
async function getSession(sessionId: string): Promise<Session | null> {
return await redis.get<Session>(`session:${sessionId}`);
}
async function deleteSession(sessionId: string): Promise<void> {
await redis.del(`session:${sessionId}`);
}
实时发布/订阅
// 发布者
async function publishEvent(channel: string, data: unknown): Promise<void> {
await redis.publish(channel, JSON.stringify(data));
}
// 使用示例
await publishEvent('user:updates', { userId: '123', action: 'updated' });
排行榜
// 添加分数
await redis.zadd('leaderboard', { score: 100, member: 'user:123' });
// 获取前10名
const topUsers = await redis.zrevrange('leaderboard', 0, 9, { withScores: true });
// 获取用户排名
const rank = await redis.zrevrank('leaderboard', 'user:123');
缓存模式
// 缓存旁路模式
async function getData<T>(
key: string,
fetcher: () => Promise<T>,
ttl: number = 300
): Promise<T> {
const cached = await redis.get<T>(key);
if (cached) return cached;
const data = await fetcher();
await redis.setex(key, ttl, data);
return data;
}
// 使用示例
const user = await getData(
`user:${id}`,
() => db.query.users.findFirst({ where: eq(users.id, id) }),
300
);