用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/doanchienthangdev/omgkit --skill caching-strategies命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | caching-strategies |
| description | Multi-layer caching with Redis, CDN, and browser caching for optimal application performance |
| category | backend |
| triggers | ["caching strategies","redis cache","cdn caching","cache invalidation","http caching","performance caching"] |
Implement multi-layer caching for optimal performance. This skill covers Redis patterns, CDN configuration, HTTP cache headers, and cache invalidation strategies.
Dramatically improve application performance through strategic caching:
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);
// Basic cache operations
class CacheService {
private prefix: string;
private defaultTTL: number;
constructor(prefix: string = 'app', defaultTTL: number = 3600) {
this.prefix = prefix;
this.defaultTTL = defaultTTL;
}
private key(key: string): string {
return `${this.prefix}:${key}`;
}
async get<T>(key: string): Promise<T | null> {
const data = await redis.get(this.key(key));
return data ? JSON.parse(data) : null;
}
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
const serialized = JSON.stringify(value);
await redis.setex(this.key(key), ttl || this.defaultTTL, serialized);
}
async del(key: string): Promise<void> {
await redis.del(this.key(key));
}
async exists(key: string): Promise<boolean> {
return (await redis.exists(this.key(key))) === 1;
}
// Get or set pattern
async getOrSet<T>(
key: string,
factory: () => Promise<T>,
ttl?: number
): Promise<T> {
const cached = await this.get<T>(key);
if (cached !== null) return cached;
const value = await factory();
await this.set(key, value, ttl);
return value;
}
// Bulk operations
async mget<T>(keys: string[]): Promise<(T | null)[]> {
const prefixedKeys = keys.map(k => this.key(k));
const values = await redis.mget(prefixedKeys);
return values.map(v => (v ? JSON.parse(v) : null));
}
async mset<T>(entries: Array<{ key: string; value: T; ttl?: number }>): Promise<void> {
const pipeline = redis.pipeline();
for (const entry of entries) {
pipeline.setex(
this.key(entry.key),
entry.ttl || this.defaultTTL,
JSON.stringify(entry.value)
);
}
await pipeline.exec();
}
// Pattern-based invalidation
async invalidatePattern(pattern: string): Promise<number> {
const keys = await redis.keys(this.key(pattern));
if (keys.length === 0) return 0;
return redis.del(...keys);
}
}
// Usage
const cache = new CacheService('users', 3600);
async function getUser(id: string): Promise<User> {
return cache.getOrSet(`user:${id}`, () => db.user.findUnique({ where: { id } }));
}
// Cache-aside with database fallback
class UserRepository {
private cache: CacheService;
constructor() {
this.cache = new CacheService('users', 3600);
}
async findById(id: string): Promise<User | null> {
// Try cache first
const cached = await this.cache.get<User>(`${id}`);
if (cached) {
metrics.increment('cache.hit', { type: 'user' });
return cached;
}
metrics.increment('cache.miss', { type: 'user' });
// Fetch from database
const user = await db.user.findUnique({ where: { id } });
// Cache the result (even null to prevent cache stampede)
if (user) {
await this.cache.(, user);
} {
..(, , );
}
user;
}
(: , : <>): <> {
user = db..({ : { id }, data });
..();
user;
}
(: ): <> {
db..({ : { id } });
..();
}
}
getWithLock<T>(
: ,
: <T>,
: =
): <T> {
cache = ();
lockKey = ;
cached = cache.<T>(key);
(cached !== ) cached;
acquired = redis.(lockKey, , , , );
(!acquired) {
( (r, ));
(key, factory, ttl);
}
{
cached = cache.<T>(key);
(cached !== ) cached;
value = ();
cache.(key, value, ttl);
value;
} {
redis.(lockKey);
}
}
// Write-through cache
class WriteThroughCache<T> {
constructor(
private cache: CacheService,
private repository: Repository<T>
) {}
async create(entity: T): Promise<T> {
// Write to database first
const saved = await this.repository.create(entity);
// Then update cache
await this.cache.set(this.getKey(saved), saved);
return saved;
}
async update(id: string, data: Partial<T>): Promise<T> {
// Write to database
const updated = await this.repository.update(id, data);
// Update cache
await this.cache.set(this.getKey(updated), updated);
return updated;
}
(: T): {
;
}
}
<T> {
: <{ : ; : T }> = [];
: .;
() {
. = ( .(), flushIntervalMs);
}
(: , : T): <> {
..(key, data);
..({ key, data });
}
(): <> {
(.. === ) ;
batch = ..(, );
{
..(batch.( b.));
} (error) {
..(...batch);
.(, error);
}
}
(): <> {
(.);
.();
}
}
// Cache control middleware
interface CacheOptions {
maxAge?: number;
sMaxAge?: number;
staleWhileRevalidate?: number;
staleIfError?: number;
private?: boolean;
noStore?: boolean;
mustRevalidate?: boolean;
}
function cacheControl(options: CacheOptions) {
return (req: Request, res: Response, next: NextFunction) => {
const directives: string[] = [];
if (options.noStore) {
directives.push('no-store');
} else {
if (options.private) {
directives.push('private');
} else {
directives.push('public');
}
if (options.maxAge !== undefined) {
directives.push(`max-age=${options.maxAge}`);
}
(options. !== ) {
directives.();
}
(options. !== ) {
directives.();
}
(options. !== ) {
directives.();
}
(options.) {
directives.();
}
}
res.(, directives.());
();
};
}
app.(
,
({ : }),
express.()
);
app.(
,
({ : }),
profileHandler
);
app.(
,
({
: ,
: ,
: ,
}),
productsHandler
);
etag ;
app.(, (req, res) => {
product = (req..);
(!product) {
res.().({ : });
}
body = .(product);
tag = (body);
res.(, tag);
res.(, );
(req.[] === tag) {
res.().();
}
res.(product);
});
// Vercel Edge Config
// vercel.json
{
"headers": [
{
"source": "/api/public/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, s-maxage=60, stale-while-revalidate=86400"
}
]
},
{
"source": "/_next/static/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}
// Cloudflare Cache Rules
// Using Page Rules or Cache Rules API
const cacheRules = {
rules: [
{
expression: '(http.request.uri.path matches "^/api/public/")',
action: 'set_cache_settings',
action_parameters: {
edge_ttl: {
mode: 'override_origin',
default: 300,
},
browser_ttl: {
mode: 'override_origin',
default: 60,
},
},
},
{
expression: '(http.request.uri.path matches "^/static/")',
action: 'set_cache_settings',
action_parameters: {
cache: true,
edge_ttl: { mode: , : * },
},
},
],
};
(): <> {
(
,
{
: ,
: {
: ,
: ,
},
: .({ : urls }),
}
);
}
(): <> {
(
,
{
: ,
: {
: ,
: ,
},
: .({ tags }),
}
);
}
// Event-based cache invalidation
import { EventEmitter } from 'events';
class CacheInvalidator extends EventEmitter {
private cache: CacheService;
constructor() {
super();
this.cache = new CacheService();
this.setupListeners();
}
private setupListeners(): void {
// User events
this.on('user:updated', async (userId: string) => {
await this.cache.del(`user:${userId}`);
await this.cache.invalidatePattern(`user:${userId}:*`);
});
this.on('user:deleted', async (userId: string) => {
await this..();
});
.(, (: ) => {
..();
product = db..({ : { : productId } });
(product) {
..();
}
});
.(, () => {
redis.();
});
}
}
invalidator = ();
{
(: , : ): <> {
product = db..({ : { id }, data });
invalidator.(, id);
product;
}
}
{ } ;
(, () => {
cache.();
}).();
(, () => {
products = ();
cache.(, products, );
}).();
// L1: In-memory (fastest, smallest)
// L2: Redis (fast, shared)
// L3: Database (slowest, source of truth)
import LRUCache from 'lru-cache';
class MultiLayerCache<T> {
private l1: LRUCache<string, T>;
private l2: CacheService;
constructor(options: {
l1MaxSize: number;
l1TTL: number;
l2Prefix: string;
l2TTL: number;
}) {
this.l1 = new LRUCache({
max: options.l1MaxSize,
ttl: options.l1TTL * 1000,
});
this.l2 = new CacheService(options.l2Prefix, options.l2TTL);
}
async get(key: string): Promise<T | null> {
// Check L1 (in-memory)
const l1Value = this.l1.get(key);
if (l1Value !== undefined) {
metrics.();
l1Value;
}
l2Value = ..<T>(key);
(l2Value !== ) {
metrics.();
..(key, l2Value);
l2Value;
}
metrics.();
;
}
(: , : T, ?: , ?: ): <> {
..(key, value, { : l1TTL ? l1TTL * : });
..(key, value, l2TTL);
}
(
: ,
: <T>,
?: ,
?:
): <T> {
cached = .(key);
(cached !== ) cached;
value = ();
.(key, value, l1TTL, l2TTL);
value;
}
(: ): <> {
..(key);
..(key);
}
}
userCache = <>({
: ,
: ,
: ,
: ,
});
(): < | > {
userCache.(
,
db..({ : { id } })
);
}
// Middleware for caching API responses
function apiCache(options: {
ttl: number;
keyGenerator?: (req: Request) => string;
condition?: (req: Request) => boolean;
}) {
const cache = new CacheService('api');
return async (req: Request, res: Response, next: NextFunction) => {
// Skip caching for non-GET or if condition fails
if (req.method !== 'GET' || (options.condition && !options.condition(req))) {
return next();
}
const key = options.keyGenerator?.(req) || req.originalUrl;
const cached = await cache.get<{ body: any; headers: Record<string, string> }>(key);
if (cached) {
Object.entries(cached.headers).forEach(([k, v]) => res.set(k, v));
res.set('X-Cache', );
res.(cached.);
}
originalJson = res..(res);
res. = {
cache.(key, { body, : res.() }, options.);
res.(, );
(body);
};
();
};
}
app.(, ({ : }), getProducts);
// Redis session store
import session from 'express-session';
import RedisStore from 'connect-redis';
app.use(session({
store: new RedisStore({ client: redis }),
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000, // 24 hours
},
}));
## Cache Implementation Checklist
### Design
- [ ] Identified cacheable data
- [ ] Defined appropriate TTLs
- [ ] Planned invalidation strategy
- [ ] Considered cache layers
### Implementation
- [ ] Added cache-aside logic
- [ ] Implemented stampede prevention
- [ ] Set up monitoring
- [ ] Added cache headers
### Operations
- [ ] Monitoring hit/miss ratios
- [ ] Alerting on cache failures
- [ ] Regular cache analysis
- [ ] Invalidation testing