← Назад к вопросам

Можно ли очистить кеш во время выполнения кода?

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);
      // Продолжить работу без кеша
    }
  }
}

Заключение

Ключевые моменты:

  1. Да, можно очищать кеш — это нормальная операция
  2. Event-based очистка — при изменении данных
  3. TTL — автоматическая очистка старых данных
  4. Стратегии — Time-based, Event-based, Tag-based
  5. Production patterns — versioning, circuit breaker, warm cache

Правильное управление кешем критично для performance и консистентности данных в production.

Можно ли очистить кеш во время выполнения кода? | PrepBro