[PHP, Программирование] 2R2L кеширование
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Кеширование – широко освещенная и известная тема. Но и в ней могут появляться новые решения. В частности – в области высокоуровневых продуктов (например, в веб-разработке). Столкнувшись с недостатками классического подхода, я попробовал вывести идеальную схему кеширования для случая, когда актуальность данных не является критической. Потом я попробовал найти описание подобной схемы, а лучше – готовые решения. Не нашел. Поэтому назвал ее сам – 2R2L (2 Range 2 Location) – двух-диапазонное двух-«пространственное» кеширование. Хотя наверняка оно уже где-то применяется.
Началось все с простой задачи – отобразить пользователю новинки неких товаров с учетом его индивидуальных предпочтений. И если с получением новинок проблем не было, то соотнесение новинок с предпочтениями (анализ статистики) уже создавал ощутимую нагрузку (для примера определим ее в 4 секунды). Особенность задачи состояла в том, что в качестве пользователей у нас могут выступать целые организации. И нередки случаи, когда одномоментно (в течение 2-3 секунд) на сервер прилетает 200-300 запросов, относящихся к одному пользователю. Т.е. генерируется один и тот же блок сразу для многих пользователей.
Очевидное решение – надо кешировать в RAM (не будем подвергать СУБД насилию, заставляя отрабатывать большой поток обращений). Классическая схема:
- Пришел запрос
- Проверяем кеш. Если данные в нем есть, и они не устарели – просто отдаем их.
- Данных нет => генерируем выдачу
- Отправляем пользователю
- Дополнительно складываем в кеш, указывая TTL
Недостаток такого решения: если данных в кеше нет, генерировать их будут все запросы, пришедшие за время первой генерации, затрачивая на это ресурсы сервера (пики нагрузки). Ну и конечно, все пользователи при «первом обращении» будут ждать.
Также отметим, что при индивидуальных кеш-значениях количество записей может вырасти на столько, что доступной ОЗУ сервера просто не хватит. Тогда логичным выглядит использование локального HDD сервера в качестве хранилища кешей. Но мы сразу теряем в скорости.
Как же быть?
Первое, что приходит в голову: было бы здорово хранить записи в 2 местах — в RAM (часто запрашиваемые) и HDD (все или только редко запрашиваемые). Концепция «горячих и холодных данных» в чистом виде. Реализаций такого подхода – множество, поэтому останавливаться на нем не будем. Просто обозначим эту составляющую как 2L. В моем случае она успешно реализуется на базе СУБД Scylla.
Но как избавиться от «просадок» в моменты, когда кеш устарел? А здесь мы и подключаем концепцию 2R, смысл которой заключается в простой вещи: для кеш-записи надо указывать не 1 значение TTL, а 2. TTL1 – метка времени, которая означает «данные устарели, надо бы перегенерировать, но использовать еще можно»; TTL2 – «все устарело настолько, что использовать уже нельзя».
Таким образом получаем немного иную схему работы кеширования:
- Пришел запрос
- Ищем данные в кеше. Если данные есть и не устарели (t<TTL1) – отдаем пользователю, как обычно и больше ничего не делаем.
- Данные есть, устарели, но можно использовать (TTL1 < t < TTL2) – отдаем пользователю И инициализируем процедуру обновления кеш-записи
- Данных нет совсем (убиты по истечении TTL2) – генерируем «как обычно» и записываем в кеш.
- После отдачи контента пользователю или в параллельном потоке выполняем процедуры обновления кеш-записей.
В результате мы имеем:
— если кеш-записи используются достаточно часто, пользователь никогда не попадет в ситуацию «ожидаем актуализации кеша» — он всегда будет получать уже готовый результат.
— если правильно организовать очередь «актуализаций», то можно добиться того, что в случае нескольких одновременных обращений к записи с 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 = $$v[‘method’];
cache_set($tmp, 60*60, 60*60*24*7);
}
}
На практике, конечно, реализация, скорее всего, будет посложнее. Например, генератор кеш-записей – отдельный скрипт, запущенный в качестве сервиса; очередь – через Rabbit, признак «такой ключ уже есть в очереди на перегенерацию» — через Redis или туже Scylla.
Итого, если объединить «двух-диапазонный» подход и концепцию «горячие/холодные» данные, как раз и получим – 2R2L.
Спасибо!
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, Проектирование и рефакторинг, Разработка игр] Как мы пришли к реактивному связыванию в Unity3D
- [Анализ и проектирование систем, Программирование, Проектирование и рефакторинг, Управление персоналом, Управление разработкой] Человечная декомпозиция работы
- [Тестирование IT-систем, Программирование, Тестирование веб-сервисов] Как мы «разогнали» команду QA, и что из этого получилось
- [DIY или Сделай сам, Программирование микроконтроллеров] Дистанционное управление громкостью IP TV приставки при помощи Attiny13A
- [Высокая производительность, Python, Программирование, Машинное обучение] Deep Learning Inference Benchmark — измеряем скорость работы моделей глубокого обучения
- [Git, Программирование, Системы управления версиями] И полгода не прошло: выпущена система управления версиями Git 2.29
- [Программирование, Scala] Scala как первый язык
- [Разработка веб-сайтов, JavaScript, Программирование] Веб-компоненты: руководство для начинающих (перевод)
- [C++, Алгоритмы, Программирование, Процессоры, Разработка веб-сайтов] Исключительно быстрая валидация UTF-8 (перевод)
- [Программирование] LDM. Моя любимая инструкция ARM (перевод)
Теги для поиска: #_php, #_programmirovanie (Программирование), #_keshirovanie (кеширование), #_php, #_programmirovanie (
Программирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:25
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Кеширование – широко освещенная и известная тема. Но и в ней могут появляться новые решения. В частности – в области высокоуровневых продуктов (например, в веб-разработке). Столкнувшись с недостатками классического подхода, я попробовал вывести идеальную схему кеширования для случая, когда актуальность данных не является критической. Потом я попробовал найти описание подобной схемы, а лучше – готовые решения. Не нашел. Поэтому назвал ее сам – 2R2L (2 Range 2 Location) – двух-диапазонное двух-«пространственное» кеширование. Хотя наверняка оно уже где-то применяется. Началось все с простой задачи – отобразить пользователю новинки неких товаров с учетом его индивидуальных предпочтений. И если с получением новинок проблем не было, то соотнесение новинок с предпочтениями (анализ статистики) уже создавал ощутимую нагрузку (для примера определим ее в 4 секунды). Особенность задачи состояла в том, что в качестве пользователей у нас могут выступать целые организации. И нередки случаи, когда одномоментно (в течение 2-3 секунд) на сервер прилетает 200-300 запросов, относящихся к одному пользователю. Т.е. генерируется один и тот же блок сразу для многих пользователей. Очевидное решение – надо кешировать в RAM (не будем подвергать СУБД насилию, заставляя отрабатывать большой поток обращений). Классическая схема:
Недостаток такого решения: если данных в кеше нет, генерировать их будут все запросы, пришедшие за время первой генерации, затрачивая на это ресурсы сервера (пики нагрузки). Ну и конечно, все пользователи при «первом обращении» будут ждать. Также отметим, что при индивидуальных кеш-значениях количество записей может вырасти на столько, что доступной ОЗУ сервера просто не хватит. Тогда логичным выглядит использование локального HDD сервера в качестве хранилища кешей. Но мы сразу теряем в скорости. Как же быть? Первое, что приходит в голову: было бы здорово хранить записи в 2 местах — в RAM (часто запрашиваемые) и HDD (все или только редко запрашиваемые). Концепция «горячих и холодных данных» в чистом виде. Реализаций такого подхода – множество, поэтому останавливаться на нем не будем. Просто обозначим эту составляющую как 2L. В моем случае она успешно реализуется на базе СУБД Scylla. Но как избавиться от «просадок» в моменты, когда кеш устарел? А здесь мы и подключаем концепцию 2R, смысл которой заключается в простой вещи: для кеш-записи надо указывать не 1 значение TTL, а 2. TTL1 – метка времени, которая означает «данные устарели, надо бы перегенерировать, но использовать еще можно»; TTL2 – «все устарело настолько, что использовать уже нельзя». Таким образом получаем немного иную схему работы кеширования:
В результате мы имеем: — если кеш-записи используются достаточно часто, пользователь никогда не попадет в ситуацию «ожидаем актуализации кеша» — он всегда будет получать уже готовый результат. — если правильно организовать очередь «актуализаций», то можно добиться того, что в случае нескольких одновременных обращений к записи с 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 = $$v[‘method’]; cache_set($tmp, 60*60, 60*60*24*7); } } На практике, конечно, реализация, скорее всего, будет посложнее. Например, генератор кеш-записей – отдельный скрипт, запущенный в качестве сервиса; очередь – через Rabbit, признак «такой ключ уже есть в очереди на перегенерацию» — через Redis или туже Scylla. Итого, если объединить «двух-диапазонный» подход и концепцию «горячие/холодные» данные, как раз и получим – 2R2L. Спасибо! =========== Источник: habr.com =========== Похожие новости:
Программирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:25
Часовой пояс: UTC + 5