← Назад к вопросам
Можно ли очистить кеш во время выполнения кода?
1.7 Middle🔥 101 комментариев
#Node.js и JavaScript#Кэширование и производительность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Очистка кеша во время выполнения кода
Да, можно и нужно очищать кеш во время выполнения. Это нормальная операция, которая используется для инвалидации устаревших данных.
Redis — основной кеш в Node.js Backend
1. Очистка отдельного ключа
import redis from 'redis';
const client = redis.createClient();
// Кешируем результат
async function getCachedUser(userId: string) {
const cached = await client.get(`user:${userId}`);
if (cached) return JSON.parse(cached);
const user = await db.users.findById(userId);
await client.setEx(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}
// Очищаем кеш при обновлении пользователя
async function updateUser(userId: string, data: Partial<User>) {
const updated = await db.users.update(userId, data);
// Инвалидируем кеш
await client.del(`user:${userId}`);
return updated;
}
2. Очистка по шаблону (pattern)
// Очистить все кеши пользователя
async function clearUserCaches(userId: string) {
// Найти все ключи, начинающиеся с user:userId:
const keys = await client.keys(`user:${userId}:*`);
if (keys.length > 0) {
await client.del(keys);
}
}
// Использование:
async function deleteUser(userId: string) {
await db.users.delete(userId);
await clearUserCaches(userId);
}
3. Очистка через SCAN (более безопасно для production)
// SCAN не блокирует Redis в отличие от KEYS
async function scanAndDelete(pattern: string) {
let cursor = '0';
const keysToDelete: string[] = [];
do {
const [newCursor, keys] = await client.scan(
cursor,
{ MATCH: pattern, COUNT: 100 }
);
cursor = newCursor;
keysToDelete.push(...keys);
} while (cursor !== '0');
if (keysToDelete.length > 0) {
await client.del(keysToDelete);
}
}
// Использование:
await scanAndDelete('user:123:*');
4. TTL и автоматическая очистка
// Redis автоматически удаляет ключи по TTL
async function cacheUserWithExpiry(userId: string) {
const user = await db.users.findById(userId);
// Кеш на 1 час (3600 секунд)
await client.setEx(
`user:${userId}`,
3600,
JSON.stringify(user)
);
// Или с явным EXPIRE:
await client.set(`user:${userId}`, JSON.stringify(user));
await client.expire(`user:${userId}`, 3600);
}
// Получить оставшееся время жизни
const ttl = await client.ttl(`user:${userId}`);
if (ttl > 0) {
console.log(`Кеш протухнет через ${ttl} секунд`);
}
Cache Invalidation Strategies
1. Time-based invalidation (TTL)
// Самая простая стратегия
const CACHE_TTL = {
USER: 3600, // 1 час
POST: 1800, // 30 минут
COMMENT: 900, // 15 минут
} as const;
async function getCachedPost(postId: string) {
const cached = await client.get(`post:${postId}`);
if (cached) return JSON.parse(cached);
const post = await db.posts.findById(postId);
await client.setEx(
`post:${postId}`,
CACHE_TTL.POST,
JSON.stringify(post)
);
return post;
}
2. Event-based invalidation (на событие)
// Очищать кеш при изменении данных
class UserService {
async updateUser(userId: string, data: Partial<User>) {
const updated = await db.users.update(userId, data);
// Инвалидируем связанные кеши
await this.cache.del(`user:${userId}`);
await this.cache.del(`user:${userId}:profile`);
await this.cache.del(`user:${userId}:posts`);
return updated;
}
async addPost(userId: string, postData: CreatePost) {
const post = await db.posts.create({ userId, ...postData });
// Инвалидируем кеш списка постов пользователя
await this.cache.del(`user:${userId}:posts`);
return post;
}
}
3. Tag-based invalidation (через теги)
// Эмулируем теги в Redis
class TaggedCache {
async set(key: string, value: any, tags: string[], ttl: number = 3600) {
// Сохраняем значение
await this.redis.setEx(key, ttl, JSON.stringify(value));
// Сохраняем теги для этого ключа
for (const tag of tags) {
await this.redis.sadd(`tag:${tag}`, key);
}
}
async invalidateTag(tag: string) {
// Получить все ключи с этим тегом
const keys = await this.redis.smembers(`tag:${tag}`);
if (keys.length > 0) {
await this.redis.del(keys);
await this.redis.del(`tag:${tag}`);
}
}
}
// Использование:
const cache = new TaggedCache(redisClient);
await cache.set(
`post:${postId}`,
post,
[`post`, `user:${post.userId}`, `category:${post.category}`]
);
// Очистить все посты пользователя:
await cache.invalidateTag(`user:${userId}`);
Очистка разных типов кеша
1. In-memory cache (Node.js)
import NodeCache from 'node-cache';
const cache = new NodeCache({ stdTTL: 600 }); // TTL: 10 минут
// Кешировать
cache.set('user:123', userData);
// Получить
const user = cache.get('user:123');
// Очистить один ключ
cache.del('user:123');
// Очистить по ключам
cache.del(['user:123', 'user:124']);
// Очистить всё
cache.flushAll();
// Очистить expired ключи (работает автоматически)
cache.close();
2. Database-level кеш
// TypeORM QueryBuilder с кешем
const posts = await db.createQueryBuilder('post')
.where('post.authorId = :authorId', { authorId: userId })
.cache('user:' + userId + ':posts', 1800) // 30 минут
.getMany();
// Очистить кеш запроса (не стандартный метод, нужно вручную):
await client.del(`user:${userId}:posts`);
3. HTTP кеш
// Очистить CDN кеш
async function purgeCloudflareCache(url: string) {
const response = await fetch('https://api.cloudflare.com/client/v4/zones/123/purge_cache', {
method: 'POST',
headers: {
'X-Auth-Email': process.env.CLOUDFLARE_EMAIL,
'X-Auth-Key': process.env.CLOUDFLARE_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
files: [url]
})
});
return response.json();
}
// Очистить при обновлении контента:
async function updatePost(postId: string, data: any) {
const updated = await db.posts.update(postId, data);
// Инвалидируем Redis кеш
await redisClient.del(`post:${postId}`);
// Инвалидируем CDN кеш
await purgeCloudflareCache(`https://example.com/posts/${postId}`);
return updated;
}
Production patterns
1. Warm cache при старте
// Загрузить горячие данные при старте приложения
async function warmCache() {
const popularPosts = await db.posts.find({
order: { views: 'DESC' },
limit: 100
});
for (const post of popularPosts) {
await redisClient.setEx(
`post:${post.id}`,
3600,
JSON.stringify(post)
);
}
console.log(`Warmed cache with ${popularPosts.length} posts`);
}
// В app.ts при запуске:
await warmCache();
2. Cache versioning
// Добавлять версию кеша для контролируемого обновления
const CACHE_VERSION = 'v2'; // Измени при необходимости
async function getCachedData(key: string) {
const cacheKey = `${CACHE_VERSION}:${key}`;
const cached = await redisClient.get(cacheKey);
if (cached) return JSON.parse(cached);
const data = await fetchData(key);
await redisClient.setEx(cacheKey, 3600, JSON.stringify(data));
return data;
}
// При изменении логики просто обнови версию
// Все старые ключи станут невидимыми
3. Circuit breaker для Redis
// Если Redis недоступен — работать без кеша
class SafeCache {
async get(key: string) {
try {
return await this.redis.get(key);
} catch (error) {
logger.warn('Redis unavailable, skipping cache');
return null;
}
}
async set(key: string, value: any, ttl: number) {
try {
await this.redis.setEx(key, ttl, JSON.stringify(value));
} catch (error) {
logger.warn('Failed to cache:', error);
// Продолжить работу без кеша
}
}
}
Заключение
Ключевые моменты:
- Да, можно очищать кеш — это нормальная операция
- Event-based очистка — при изменении данных
- TTL — автоматическая очистка старых данных
- Стратегии — Time-based, Event-based, Tag-based
- Production patterns — versioning, circuit breaker, warm cache
Правильное управление кешем критично для performance и консистентности данных в production.