Русский
Русский
English
Статистика
Реклама

2R2L кеширование

Кеширование широко освещенная и известная тема. Но и в ней могут появляться новые решения. В частности в области высокоуровневых продуктов (например, в веб-разработке). Столкнувшись с недостатками классического подхода, я попробовал вывести идеальную схему кеширования для случая, когда актуальность данных не является критической. Потом я попробовал найти описание подобной схемы, а лучше готовые решения. Не нашел. Поэтому назвал ее сам 2R2L (2 Range 2 Location) двух-диапазонное двух-пространственное кеширование. Хотя наверняка оно уже где-то применяется.

Началось все с простой задачи отобразить пользователю новинки неких товаров с учетом его индивидуальных предпочтений. И если с получением новинок проблем не было, то соотнесение новинок с предпочтениями (анализ статистики) уже создавал ощутимую нагрузку (для примера определим ее в 4 секунды). Особенность задачи состояла в том, что в качестве пользователей у нас могут выступать целые организации. И нередки случаи, когда одномоментно (в течение 2-3 секунд) на сервер прилетает 200-300 запросов, относящихся к одному пользователю. Т.е. генерируется один и тот же блок сразу для многих пользователей.

Очевидное решение надо кешировать в RAM (не будем подвергать СУБД насилию, заставляя отрабатывать большой поток обращений). Классическая схема:

  1. Пришел запрос
  2. Проверяем кеш. Если данные в нем есть, и они не устарели просто отдаем их.
  3. Данных нет => генерируем выдачу
  4. Отправляем пользователю
  5. Дополнительно складываем в кеш, указывая TTL

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

Также отметим, что при индивидуальных кеш-значениях количество записей может вырасти на столько, что доступной ОЗУ сервера просто не хватит. Тогда логичным выглядит использование локального HDD сервера в качестве хранилища кешей. Но мы сразу теряем в скорости.

Как же быть?

Первое, что приходит в голову: было бы здорово хранить записи в 2 местах в RAM (часто запрашиваемые) и HDD (все или только редко запрашиваемые). Концепция горячих и холодных данных в чистом виде. Реализаций такого подхода множество, поэтому останавливаться на нем не будем. Просто обозначим эту составляющую как 2L. В моем случае она успешно реализуется на базе СУБД Scylla.

Но как избавиться от просадок в моменты, когда кеш устарел? А здесь мы и подключаем концепцию 2R, смысл которой заключается в простой вещи: для кеш-записи надо указывать не 1 значение TTL, а 2. TTL1 метка времени, которая означает данные устарели, надо бы перегенерировать, но использовать еще можно; TTL2 все устарело настолько, что использовать уже нельзя.

Таким образом получаем немного иную схему работы кеширования:

  1. Пришел запрос
  2. Ищем данные в кеше. Если данные есть и не устарели (t<TTL1) отдаем пользователю, как обычно и больше ничего не делаем.
  3. Данные есть, устарели, но можно использовать (TTL1 < t < TTL2) отдаем пользователю И инициализируем процедуру обновления кеш-записи
  4. Данных нет совсем (убиты по истечении TTL2) генерируем как обычно и записываем в кеш.
  5. После отдачи контента пользователю или в параллельном потоке выполняем процедуры обновления кеш-записей.

В результате мы имеем:

  • если кеш-записи используются достаточно часто, пользователь никогда не попадет в ситуацию ожидаем актуализации кеша он всегда будет получать уже готовый результат.
  • если правильно организовать очередь актуализаций, то можно добиться того, что в случае нескольких одновременных обращений к записи с TTL1 < t < TTL2, в очереди будет находиться только 1 задача на обновление, а не несколько одинаковых.

В качестве примера: для ленты новинок можно указать TTL1 = 1 час (все же не сильно интенсивно новый контент появляется), а TTL2 1 неделя.

В простейшем случае код на PHP для реализации 2R может быть таким:

$tmp = cache_get($key);If (!$tmp){$items = generate_items();cache_set($items, 60*60, 60*60*24*7);}else{$items = $tmp[items];If (time()-$tmp[tm] > 60*60){$need_rebuild[] = [to=>$key, method=>generate_items];}}// отдаем данные пользователюecho json_encode($items);// поскольку данные пользователю уже отправлены, можно и повычислятьIf (isset($need_rebuild) && count($need_rebuild)>0){foreach($need_rebuild as $k=>$v){$tmp = ['tm'=>time(), 'items'=>$$v[method]];cache_set($tmp, 60*60, 60*60*24*7);}}

На практике, конечно, реализация, скорее всего, будет посложнее. Например, генератор кеш-записей отдельный скрипт, запущенный в качестве сервиса; очередь через Rabbit, признак такой ключ уже есть в очереди на перегенерацию через Redis или Scylla.

Итого, если объединить двух-диапазонный подход и концепцию горячие/холодные данные, как раз и получим 2R2L.

Спасибо!
Источник: habr.com
К списку статей
Опубликовано: 23.10.2020 18:08:20
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Php

Программирование

Кеширование

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru