Ответ
Использовал несколько стратегий кеширования в Node.js в зависимости от требований:
1. In-memory кеш для быстрых операций:
class MemoryCache {
constructor(ttl = 60000) {
this.cache = new Map();
this.ttl = ttl;
}
set(key, value, customTtl) {
const expiry = Date.now() + (customTtl || this.ttl);
this.cache.set(key, { value, expiry });
// Автоочистка просроченных записей
setTimeout(() => {
if (this.cache.get(key)?.expiry < Date.now()) {
this.cache.delete(key);
}
}, customTtl || this.ttl);
}
get(key) {
const item = this.cache.get(key);
if (!item || item.expiry < Date.now()) {
this.cache.delete(key);
return null;
}
return item.value;
}
}
// Использование
const cache = new MemoryCache();
const user = cache.get(`user:${userId}`) || await fetchUser(userId);
2. Redis для распределенного кеширования:
const redis = require('redis');
const { promisify } = require('util');
class RedisCache {
constructor() {
this.client = redis.createClient({
url: process.env.REDIS_URL
});
this.getAsync = promisify(this.client.get).bind(this.client);
this.setExAsync = promisify(this.client.setex).bind(this.client);
}
async getOrSet(key, fetchFn, ttl = 3600) {
const cached = await this.getAsync(key);
if (cached) return JSON.parse(cached);
const data = await fetchFn();
await this.setExAsync(key, ttl, JSON.stringify(data));
return data;
}
}
// Пример с кешированием запросов к БД
app.get('/api/products', async (req, res) => {
const products = await redisCache.getOrSet(
`products:page:${req.query.page}`,
() => Product.find().skip(offset).limit(limit),
300 // 5 минут
);
res.json(products);
});
3. HTTP-кеширование с заголовками:
app.get('/api/static-data', (req, res) => {
const data = getStaticData();
// Устанавливаем заголовки кеширования
res.set({
'Cache-Control': 'public, max-age=3600', // 1 час
'ETag': generateETag(data),
'Last-Modified': new Date().toUTCString()
});
// Проверяем условные запросы
if (req.fresh) { // Используется middleware fresh
return res.status(304).end(); // Not Modified
}
res.json(data);
});
4. Кеширование на уровне БД:
- Query caching в Mongoose
- Connection pooling для повторного использования соединений
- DataLoader для batch-запросов в GraphQL
5. Стратегии инвалидации:
- TTL-based (время жизни)
- Write-through (обновление кеша при записи)
- Cache-aside (ленивая загрузка)
- Tag-based инвалидация для сложных зависимостей
Производительность:
- In-memory: ~0.1ms на операцию
- Redis: ~1-5ms (с учетом сетевой задержки)
- Без кеша: 50-500ms (зависит от источника данных)
Для production-приложений обычно комбинирую несколько подходов: in-memory для горячих данных, Redis для распределенного доступа, HTTP-кеширование для статики.