Практическая работа №22
Цель работы: научиться использовать Redis-совместимое хранилище для серверного кэширования данных веб-приложения, реализовывать стратегию Cache Aside, управлять временем жизни кэша, выполнять его инвалидирование и оценивать влияние кэширования на время выполнения запросов.
Основные теоретические положения
Section titled “Основные теоретические положения”1. Redis и Memurai
Redis — высокопроизводительное хранилище данных типа «ключ — значение», которое хранит активно используемые данные в оперативной памяти.
Одно из распространённых применений Redis — кэширование данных веб-приложений.
В данной работе используется Memurai, реализующий Redis-совместимый протокол.
Приложение подключается к нему по адресу:
127.0.0.1:6379Для приложения взаимодействие выглядит следующим образом:
Веб-приложение | | Redis Protocol v127.0.0.1:6379 | v MemuraiВ программном коде необходимо использовать Redis-клиент для выбранного языка.
Например:
| Язык | Клиент |
|---|---|
| JavaScript / TypeScript | node-redis |
| Python | redis-py |
| PHP | Predis или phpredis |
| Go | go-redis |
| C# / .NET | StackExchange.Redis |
| Java | Jedis или Lettuce |
2. Кэширование
Кэш — временное хранилище данных, позволяющее не выполнять повторно дорогостоящую операцию.
Предположим, веб-приложение получает сведения о товаре из базы данных:
GET /products/1 | v Веб-приложение | v База данных | v ОтветЕсли получение данных занимает:
600 мсто каждый повторный запрос будет снова выполнять эту операцию.
При использовании Redis:
GET /products/1 | v Веб-приложение | v Redis / \ HIT MISS | | | v | Источник данных | | | v | сохранить в Redis | | +--------+ | v ОтветПовторное получение значения из памяти обычно выполняется значительно быстрее, чем повторное обращение к более медленному источнику данных.
3. Cache HIT и Cache MISS
Если требуемые данные уже находятся в Redis, происходит:
Cache HITПриложение получает данные непосредственно из кэша.
Если ключ отсутствует:
Cache MISSприложение должно получить данные из основного источника и сохранить их в Redis.
4. Стратегия Cache Aside
В данной работе необходимо использовать стратегию Cache Aside.
Алгоритм чтения:
1. Получить запрос клиента.
2. Сформировать ключ Redis.
3. Проверить наличие ключа в Redis.
4. Если данные найдены: вернуть данные из Redis.
5. Если данные отсутствуют: получить данные из основного источника; сохранить их в Redis; вернуть данные клиенту.Например, для товара с идентификатором 15 можно использовать ключ:
cache:product:155. TTL
Кэш не должен хранить данные бесконечно.
TTL — Time To Live — время жизни ключа.
Например:
SET cache:product:15 "..." EX 30означает, что значение автоматически будет удалено через:
30 секундОставшееся время можно проверить командой:
TTL cache:product:156. Инвалидирование кэша
Предположим, в основном источнике данных товар имеет цену:
5000Приложение помещает данные в Redis.
Затем цена изменяется:
5000 -> 6500Если оставить старое значение в Redis, клиент продолжит получать:
5000до истечения TTL.
Такие данные называются устаревшими.
Поэтому после изменения исходных данных соответствующий ключ необходимо удалить:
DEL cache:product:15Следующий запрос вызовет Cache MISS, получит новое значение и снова заполнит кэш.
7. Измерение эффективности
Оптимизация должна подтверждаться измерениями.
Необходимо сравнить:
Tsource— время получения данных из основного источника;
и:
Tcache— время получения тех же данных из Redis.
Во сколько раз кэш оказался быстрее:
K = Tsource / TcacheПроцент сокращения времени:
P = (Tsource - Tcache) / Tsource * 100%Задания для выполнения
Section titled “Задания для выполнения”Для выполнения работы можно использовать любой язык программирования и веб-фреймворк.
Примеры:
JavaScript / TypeScript + Express / Fastify / NestJSPython + Flask / FastAPI / DjangoPHP + Laravel / SymfonyGo + net/http / Gin / FiberC# + ASP.NET CoreJava + Spring BootКонкретный выбор технологии на результат выполнения работы не влияет.
Для всех вариантов используются одинаковые:
- HTTP-маршруты;
- Redis-ключи;
- алгоритмы;
- TTL;
- правила инвалидирования;
- критерии проверки.
Задание 1. Проверка работы Memurai
Откройте терминал Windows:
PowerShellили:
Command PromptВыполните:
memurai-cli PINGОжидаемый ответ:
PONGСоздайте тестовый ключ:
memurai-cli SET lab:test "Redis works"Получите его значение:
memurai-cli GET lab:testОжидаемый результат:
Redis worksУдалите ключ:
memurai-cli DEL lab:testПовторите:
memurai-cli GET lab:testПосле удаления значение должно отсутствовать.
Создайте ключ со временем жизни 30 секунд:
memurai-cli SET lab:ttl "temporary value" EX 30Получите оставшееся время:
memurai-cli TTL lab:ttlПовторите команду через несколько секунд:
memurai-cli TTL lab:ttlЗапишите результаты:
Первое значение TTL: __________
Второе значение TTL: __________
Третье значение TTL: __________После истечения времени выполните:
memurai-cli GET lab:ttlОбъясните полученный результат.
Задание 2. Создание исходного веб-приложения
Создайте небольшое веб-приложение на выбранном языке программирования.
Приложение должно содержать следующие товары:
[ { "id": 1, "name": "Mechanical Keyboard", "price": 8500 }, { "id": 2, "name": "Wireless Mouse", "price": 4200 }, { "id": 3, "name": "USB-C Hub", "price": 5600 }]Способ хранения исходных данных выберите самостоятельно.
Допускается использовать:
массив;список;словарь;Map;коллекцию объектов;JSON-файл;SQLite;другую локальную СУБД.Для данной работы использование отдельной СУБД не является обязательным.
Реализуйте функцию или метод:
getProductById(id)который возвращает товар по идентификатору.
Перед возвратом результата добавьте искусственную задержку:
600–800 мсНапример, логика функции должна соответствовать следующему псевдокоду:
function getProductById(id):
wait 700 milliseconds
find product by id
return productЗадержка имитирует:
- запрос к СУБД;
- обращение к внешнему API;
- сложное вычисление;
- другую дорогостоящую операцию.
Реализуйте маршрут:
GET /products/:idНапример:
GET /products/1Ответ должен иметь вид:
{ "source": "database", "durationMs": 703, "data": { "id": 1, "name": "Mechanical Keyboard", "price": 8500 }}Поле:
durationMsдолжно содержать время обработки запроса внутри приложения.
Если товар не существует, сервер должен вернуть HTTP-код:
404 Not FoundЗадание 3. Измерение работы приложения без кэширования
Последовательно выполните запрос:
GET /products/1не менее 5 раз.
Запишите результаты.
| № запроса | Источник | Время выполнения, мс |
|---|---|---|
| 1 | database | |
| 2 | database | |
| 3 | database | |
| 4 | database | |
| 5 | database |
Рассчитайте среднее время:
Tsource = __________________ мсОтветьте на вопрос:
Почему время выполнения повторных запросов практически не уменьшается?
Задание 4. Подключение приложения к Memurai
Установите Redis-клиент для выбранного языка программирования.
Приложение должно подключаться к:
Host: 127.0.0.1Port: 6379или использовать URI:
redis://127.0.0.1:6379После подключения выполните из программного кода команду:
PINGУбедитесь, что сервер возвращает:
PONGОбработайте возможную ошибку подключения таким образом, чтобы приложение выводило понятное сообщение об ошибке.
Задание 5. Реализация Cache Aside
Измените обработку маршрута:
GET /products/:idДля каждого товара используйте ключ:
cache:product:{id}Например:
cache:product:1cache:product:2cache:product:3Реализуйте следующий алгоритм:
получить id товара
| v
сформировать ключcache:product:{id}
| v
получить значение из Redis
| +---------- значение найдено ----------+ | | | v | десериализовать | | | v | Cache HIT | | | v | вернуть ответ | | +---------- значение отсутствует | v получить исходные данные | v сериализовать данные | v сохранить в Redis с TTL 30 секунд | v Cache MISS | v вернуть ответДля хранения объекта в Redis сериализуйте его в формат:
JSONTTL должен составлять:
30 секундПри получении данных из основного источника API должен возвращать:
{ "source": "database", "durationMs": 704, "data": { "id": 1, "name": "Mechanical Keyboard", "price": 8500 }}При получении данных из Redis:
{ "source": "cache", "durationMs": 3, "data": { "id": 1, "name": "Mechanical Keyboard", "price": 8500 }}Дополнительно добавьте HTTP-заголовок:
при Cache HIT:
X-Cache: HITпри Cache MISS:
X-Cache: MISSЗадание 6. Проверка Cache HIT и Cache MISS
Перед началом проверки удалите возможный ключ:
memurai-cli DEL cache:product:1Выполните:
GET /products/1Первый запрос должен вернуть:
source: databaseX-Cache: MISSСразу повторите запрос.
Теперь должно быть:
source: cacheX-Cache: HITВыполните запрос ещё несколько раз.
Зафиксируйте результаты:
| № | X-Cache | Source | Время, мс |
|---|---|---|---|
| 1 | MISS | database | |
| 2 | HIT | cache | |
| 3 | HIT | cache | |
| 4 | HIT | cache | |
| 5 | HIT | cache |
Объясните, почему первый запрос выполняется значительно дольше последующих.
Задание 7. Исследование содержимого кэша
После выполнения:
GET /products/1проверьте наличие ключа:
memurai-cli EXISTS cache:product:1Получите его содержимое:
memurai-cli GET cache:product:1Пример результата:
{"id":1,"name":"Mechanical Keyboard","price":8500}Проверьте оставшееся время:
memurai-cli TTL cache:product:1Подождите несколько секунд и снова выполните:
memurai-cli TTL cache:product:1После истечения 30 секунд снова выполните:
memurai-cli GET cache:product:1Затем:
GET /products/1Запишите:
Источник после истечения TTL: __________________
Время выполнения: __________________ мсОбъясните, почему после истечения TTL запрос снова выполняется медленно.
Задание 8. Реализация инвалидирования кэша
Добавьте возможность изменения товара:
PUT /products/:idНапример:
PUT /products/1Content-Type: application/jsonТело запроса:
{ "name": "Mechanical Keyboard PRO", "price": 10000}После изменения товара необходимо удалить соответствующий ключ Redis:
DEL cache:product:{id}Алгоритм:
PUT /products/1
| v
изменить исходные данные
| v
DEL cache:product:1
| v
вернуть обновлённый товарПроверьте работу по следующему сценарию.
Шаг 1.
Выполните:
GET /products/1Убедитесь, что данные появились в кэше.
Шаг 2.
Проверьте:
memurai-cli GET cache:product:1Шаг 3.
Измените цену товара:
8500 -> 10000через:
PUT /products/1Шаг 4.
Проверьте:
memurai-cli GET cache:product:1Ключ должен отсутствовать.
Шаг 5.
Снова выполните:
GET /products/1Запрос должен вернуть:
X-Cache: MISSsource: databaseи новую цену:
10000Шаг 6.
Сразу повторите:
GET /products/1Теперь ожидается:
X-Cache: HITsource: cacheОбъясните, почему после изменения товара кэш необходимо удалить.
Задание 9. Сравнение производительности
Удалите кэш:
memurai-cli DEL cache:product:1Выполните первый запрос:
GET /products/1Зафиксируйте его время:
Cache MISS: __________ мсПосле этого выполните не менее 5 запросов с Cache HIT.
| № | Результат | Время, мс |
|---|---|---|
| 1 | MISS | |
| 2 | HIT | |
| 3 | HIT | |
| 4 | HIT | |
| 5 | HIT | |
| 6 | HIT |
Рассчитайте среднее время запросов, выполненных через кэш:
Tcache = __________________ мсИспользуйте полученное ранее:
Tsource = __________________ мсРассчитайте:
K = Tsource / TcacheПолучите:
Кэширование ускорило получение данных примерно в __________ раз.Дополнительно рассчитайте:
P = (Tsource - Tcache) / Tsource * 100%Время выполнения уменьшилось примерно на __________ %.Сделайте вывод, дала ли выполненная оптимизация измеримый эффект.
Задание 10. Самостоятельное расширение кэширования
Реализуйте маршрут:
GET /productsкоторый возвращает список всех товаров.
При первом запросе данные должны поступать из основного источника.
Для кэширования списка используйте ключ:
cache:products:allУстановите TTL:
20 секундПервый запрос:
X-Cache: MISSПоследующие:
X-Cache: HITПри выполнении:
PUT /products/:idнеобходимо удалить:
cache:product:{id}и:
cache:products:allТо есть после изменения товара логика инвалидирования должна соответствовать:
DEL cache:product:{id}
DEL cache:products:allПроверьте следующий сценарий:
1. GET /products
2. GET /products
3. PUT /products/2
4. GET /products
5. GET /productsОжидаемая последовательность:
MISSHITизменение данныхMISSHITОбъясните, почему при изменении одного товара необходимо удалить не только:
cache:product:2но и:
cache:products:allТребования к результату
Section titled “Требования к результату”К концу работы приложение должно поддерживать:
GET /products/:idPUT /products/:idGET /productsВ приложении должны быть реализованы:
подключение к Memurai;получение данных из Redis;сохранение данных в Redis;JSON-сериализация и десериализация;Cache HIT;Cache MISS;Cache Aside;TTL;инвалидирование кэша;заголовок X-Cache;измерение времени выполнения запросов.Структура ключей должна иметь следующий вид:
cache:product:1cache:product:2cache:product:3
cache:products:allЧто необходимо продемонстрировать при защите
Section titled “Что необходимо продемонстрировать при защите”Продемонстрируйте преподавателю:
1. Первый запрос к товару — Cache MISS.
2. Повторный запрос — Cache HIT.
3. Наличие значения через memurai-cli.
4. Работу TTL.
5. Автоматическое исчезновение ключа после истечения TTL.
6. Инвалидирование после PUT-запроса.
7. Новый Cache MISS после изменения данных.
8. Последующий Cache HIT.
9. Результаты измерения времени до и после оптимизации.Будьте готовы изменить TTL, название Redis-ключа или добавить новый кэшируемый маршрут по требованию преподавателя.
Контрольные вопросы
Section titled “Контрольные вопросы”- Что такое Redis?
- Для каких задач в веб-приложениях может использоваться Redis?
- Что называется кэшем?
- Что означает Cache HIT?
- Что означает Cache MISS?
- В чём заключается стратегия Cache Aside?
- Почему данные сначала проверяются в Redis, а только затем запрашиваются из основного источника?
- Что такое TTL?
- Что произойдёт с ключом после истечения TTL?
- Как узнать оставшееся время жизни Redis-ключа?
- Для чего при сохранении объекта используется сериализация?
- Почему слишком маленький TTL может снизить эффективность кэширования?
- Чем опасен слишком большой TTL?
- Что такое инвалидирование кэша?
- Почему после изменения данных недостаточно изменить только основной источник?
- Почему после изменения одного товара необходимо инвалидировать кэш этого товара?
- Почему при кэшировании списка товаров необходимо дополнительно удалить кэш списка?
- Почему первый запрос обычно выполняется медленнее повторного?
- Можно ли считать уменьшение времени запроса оптимизацией, если оно не было измерено до и после изменения приложения?
- В каких ситуациях использование Redis для кэширования может практически не давать выигрыша?
Рекомендуемая литература
Section titled “Рекомендуемая литература”- Redis Documentation — Cache Aside.
- Redis Documentation — Redis client libraries.
- Redis Documentation — команда
GET. - Redis Documentation — команда
SET. - Redis Documentation — команда
DEL. - Redis Documentation — команда
TTL. - Redis Documentation — команда
EXPIRE. - Memurai Documentation — Installing Memurai for Windows.
- Memurai Documentation — Memurai Command Line Interface.