Какие существуют стратегии кэширования? Опишите их плюсы и минусы.

Ответ

Существует несколько основных стратегий кэширования, каждая со своими компромиссами. Выбор зависит от требований к консистентности данных, производительности и сложности реализации.

1. Cache-Aside (Lazy Loading / Ленивая загрузка)

Это самая распространенная и простая в реализации стратегия.

Как работает:

  1. Приложение запрашивает данные сначала из кэша.
  2. Cache miss (промах): Если данных в кэше нет, приложение читает их из основной базы данных (БД).
  3. Затем приложение сохраняет эти данные в кэш для будущих запросов.
  4. Приложение возвращает данные.
  • Плюсы:
    • Простота реализации.
    • Кэшируются только те данные, которые реально запрашиваются.
    • Устойчивость к падению кэша (приложение продолжит работать, хотя и медленнее).
  • Минусы:
    • Cache miss penalty: Первый запрос на получение данных всегда будет медленным.
    • Возможна неконсистентность данных, если данные в БД обновились, а кэш не был инвалидирован.

Пример на Go (стратегия Cache-Aside):

// GetUser пытается получить пользователя из кэша, при неудаче - из БД.
func GetUser(db *sql.DB, cache *redis.Client, userID string) (*User, error) {
    // 1. Пытаемся получить из кэша
    cachedData, err := cache.Get(ctx, "user:"+userID).Bytes()
    if err == nil {
        // Cache hit: данные найдены в кэше
        var user User
        json.Unmarshal(cachedData, &user)
        return &user, nil
    }

    // 2. Cache miss: получаем из БД
    var user User
    err = db.QueryRow("SELECT id, name FROM users WHERE id = ?", userID).Scan(&user.ID, &user.Name)
    if err != nil {
        return nil, err
    }

    // 3. Сохраняем в кэш на будущее
    userJSON, _ := json.Marshal(user)
    // Устанавливаем TTL (Time-To-Live), чтобы кэш не устаревал вечно
    cache.Set(ctx, "user:"+userID, userJSON, time.Hour)

    return &user, nil
}

2. Write-Through (Сквозная запись)

  • Как работает: Приложение всегда пишет данные одновременно и в кэш, и в БД. Запись считается успешной только после подтверждения от обоих хранилищ.
  • Плюсы: Высокая консистентность данных между кэшем и БД.
  • Минусы: Увеличивается задержка операции записи (write latency), так как нужно дождаться ответа от двух систем.

3. Write-Back (Отложенная запись)

  • Как работает: Приложение пишет данные только в кэш, который отвечает немедленно. Спустя некоторое время или по накоплению определенного объема данных, кэш асинхронно записывает их в БД.
  • Плюсы: Очень низкая задержка и высокая пропускная способность операций записи.
  • Минусы: Высокий риск потери данных, если кэш выйдет из строя до того, как данные будут сохранены в БД.

Ответ 18+ 🔞

А, ну вот, классика жанра! Подходи, садись, сейчас я тебе про кэширование так расскажу, что ты сам себе покажешься гением, хотя вчера ещё думал, что это какая-то магия для задротов.

Так, слушай сюда, у нас тут есть три главных подхода, как эту всю хуйню организовать. Выбираешь в зависимости от того, что тебе важнее: чтобы данные были свежие как утренняя булка, чтобы всё летало как угорелое, или чтобы твоя голова не взорвалась от сложности кода.

1. Ленивый засранец (Cache-Aside / Ленивая загрузка)

Это когда твоё приложение ведёт себя как студент перед сессией. Работает по принципу «вспомнил — сделал, не вспомнил — похуй, пока не припрет».

Как оно дрыхнет:

  1. Запрос приходит. Приложение лезет в кэш: «Э, браток, а у тебя тут данные по этому юзеру есть?».
  2. Промах (Cache miss): Кэш такой: «Не, не, не видел, иди нахуй». Приложению, блядь, приходится вставать с дивана и тащиться в основную базу данных, как в библиотеку в три часа ночи.
  3. Нашло данные в БД, вернулось, и только потом, такое хитрожопое, закидывает их в кэш. «На, держи, запомни на будущее, а то опять спрашивать будешь».
  4. Отдаёт данные тому, кто просил.
  • Плюсы (почему все так и делают):
    • Реализовать — проще пареной репы. Даже я, после трёх бутылок, смогу.
    • Кэш не засоряется всяким мусором, который никому не нужен. Только то, что реально спрашивают.
    • Кэш упал? Да похуй! Приложение просто начнёт чуть медленнее работать, но не сдохнет. Живучесть, блядь!
  • Минусы (подводные ебли):
    • Штраф за тупость: Самый первый запрос всегда будет тормозить, как черепаха в сиропе.
    • Может возникнуть ебанистическая ситуация, когда данные в БД уже обновились, а в кэше лежит старый пиздёж. Надо самому следить, когда кэш чистить.

Вот, смотри, как это выглядит в коде, чтоб ты понимал масштаб трагедии:

// GetUser пытается получить пользователя из кэша, при неудаче - из БД.
func GetUser(db *sql.DB, cache *redis.Client, userID string) (*User, error) {
    // 1. Пытаемся получить из кэша (надеемся на халяву)
    cachedData, err := cache.Get(ctx, "user:"+userID).Bytes()
    if err == nil {
        // О, удача! Данные в кэше! Работаем, не отсвечиваем.
        var user User
        json.Unmarshal(cachedData, &user)
        return &user, nil
    }

    // 2. Не повезло, не фартануло. Топаем в БД, как лох.
    var user User
    err = db.QueryRow("SELECT id, name FROM users WHERE id = ?", userID).Scan(&user.ID, &user.Name)
    if err != nil {
        return nil, err
    }

    // 3. Теперь, чтобы в следующий раз не выглядеть идиотом, кладём находку в кэш.
    userJSON, _ := json.Marshal(user)
    // И ставим срок годности, а то протухнет, блядь!
    cache.Set(ctx, "user:"+userID, userJSON, time.Hour)

    return &user, nil
}

2. Педантичный зануда (Write-Through / Сквозная запись)

Это стратегия для перфекционистов, у которых всё по полочкам. Прям как мой сосед, который моет машину в перчатках.

  • Как работает: Любая операция записи — это сразу двойной удар. Приложение синхронно, одним махом, пишет и в кэш, и в базу данных. И только когда оба скажут «ок», считается, что запись прошла.
  • Плюсы: Консистентность, блядь, на высоте! В кэше и в БД — одно и то же. Сон спокойный.
  • Минусы: Скорость записи, конечно, страдает. Представь, что ты должен не просто бросить бумажку в урну, а ещё и позвонить в ЖКХ, чтобы они подтвердили, что она туда упала. Задержка, епта!

3. Рисковый похуист (Write-Back / Отложенная запись)

А это уже для отчаянных парней, которые любят жить на грани. Как ехать на спущенном колесе, надеясь, что докатишь.

  • Как работает: Приложение пишет данные только в кэш, и кэш сразу такой: «Всё ок, братан, свободен!». А сам потом, в фоне, не спеша, смачно, сваливает эту пачку данных в основную БД. Может, через минуту, может, через час.
  • Плюсы: Запись — просто космическая скорость! Клиенты счастливы, метрики летят вверх.
  • Минусы: Риск потери данных — овердохуища. Если кэш, сука, возьмёт и накроется медным тазом до того, как сольёт данные в БД — всё, пиши пропало. Данные испарились, как твоя зарплата в пятницу вечером. Только для тех, у кого яйца титановые.

Вот и вся философия. Выбирай, с кем ты сегодня: с ленивцем, занудой или авантюристом. Главное — понимать, за что платишь.