0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>На сегодняшний день время отклика на запрос является критичной метрикой для любого проекта. Пользователи не очень любят, когда web-страница формируется долго (несколько секунд или дольше). Поэтому разработчики стремятся уменьшить время ответа на запрос клиента, для чего прибегают к кэшированию.</p>
1
<p>На сегодняшний день время отклика на запрос является критичной метрикой для любого проекта. Пользователи не очень любят, когда web-страница формируется долго (несколько секунд или дольше). Поэтому разработчики стремятся уменьшить время ответа на запрос клиента, для чего прибегают к кэшированию.</p>
2
<p>Представим, что разрабатывается торговая платформа и есть личный кабинет с аналитикой сделок пользователя. Нужно выводить количество сделок, прибыль, средний оборот и тд. Большинство метрик являются агрегационными, то есть их нужно вычислять. Если использовать только РСУБД, то среднее время ответа будет сотни миллисекунд, а при большой нагрузке - секунды. Можно подключить подходящие аналитические базы данных, но чаще всего это слишком дорого в средних проектах.</p>
2
<p>Представим, что разрабатывается торговая платформа и есть личный кабинет с аналитикой сделок пользователя. Нужно выводить количество сделок, прибыль, средний оборот и тд. Большинство метрик являются агрегационными, то есть их нужно вычислять. Если использовать только РСУБД, то среднее время ответа будет сотни миллисекунд, а при большой нагрузке - секунды. Можно подключить подходящие аналитические базы данных, но чаще всего это слишком дорого в средних проектах.</p>
3
<p>Самая распространенная практика решения сегодня - это кэш. Сложные вычисления кэшируются, то есть записываются в оперативную память. Обращение к оперативной памяти займет сотни наносекунд, примерно в миллион раз быстрее, чем вычислить данные из РСУБД. Новая схема взаимодействия будет включать кэширующий сервер. Алгоритм следующий:</p>
3
<p>Самая распространенная практика решения сегодня - это кэш. Сложные вычисления кэшируются, то есть записываются в оперативную память. Обращение к оперативной памяти займет сотни наносекунд, примерно в миллион раз быстрее, чем вычислить данные из РСУБД. Новая схема взаимодействия будет включать кэширующий сервер. Алгоритм следующий:</p>
4
<ol><li>клиент делает запрос</li>
4
<ol><li>клиент делает запрос</li>
5
<li>бекенд проверяет кэш. Если значение есть в кэше, то оно просто возвращается клиенту</li>
5
<li>бекенд проверяет кэш. Если значение есть в кэше, то оно просто возвращается клиенту</li>
6
<li>если значения в кэше нет, то сервер вычисляет все из базы, записывает числа в кэш и отдает клиенту. Теперь все последующие запросы будут возвращать результаты вычислений из кэша</li>
6
<li>если значения в кэше нет, то сервер вычисляет все из базы, записывает числа в кэш и отдает клиенту. Теперь все последующие запросы будут возвращать результаты вычислений из кэша</li>
7
</ol><h2>Подводные камни</h2>
7
</ol><h2>Подводные камни</h2>
8
<p>Как и любое решение в разработке, кэширование - не серебряная пуля и имеет свои недостатки. Несколько моментов, которые стоит держать в уме при использовании кэша:</p>
8
<p>Как и любое решение в разработке, кэширование - не серебряная пуля и имеет свои недостатки. Несколько моментов, которые стоит держать в уме при использовании кэша:</p>
9
<ul><li>приложение должно уметь работать без кэша. Кэширующий сервер упал или недоступен? Пользователь все равно должен получать все данные просто с небольшой задержкой. Завязывать какую-то логику продукта на кэш (например, хранить данные аналитики только в кэше) - нельзя, потому что это ненадежное хранилище.</li>
9
<ul><li>приложение должно уметь работать без кэша. Кэширующий сервер упал или недоступен? Пользователь все равно должен получать все данные просто с небольшой задержкой. Завязывать какую-то логику продукта на кэш (например, хранить данные аналитики только в кэше) - нельзя, потому что это ненадежное хранилище.</li>
10
<li>с введением нового звена в архитектуру всегда следует усложнение разработки и поддержки продукта. Проверка и запись значений в кэше - это дополнительная логика в коде, которая требует дополнительного времени на тестирование и исправление потенциальных ошибок.</li>
10
<li>с введением нового звена в архитектуру всегда следует усложнение разработки и поддержки продукта. Проверка и запись значений в кэше - это дополнительная логика в коде, которая требует дополнительного времени на тестирование и исправление потенциальных ошибок.</li>
11
<li>сколько времени должно храниться значение в кэше? Что делать, если значение невалидно, так как пользователь открыл новую сделку или получил прибыль? Эти вопросы будут возникать постоянно, и в каждом проекте ответы будут уникальными.</li>
11
<li>сколько времени должно храниться значение в кэше? Что делать, если значение невалидно, так как пользователь открыл новую сделку или получил прибыль? Эти вопросы будут возникать постоянно, и в каждом проекте ответы будут уникальными.</li>
12
</ul><p>Redis чаще всего используют именно как кэширующий сервер. Он имеет богатый встроенный функционал для этих целей. Рассмотрим основные команды, с помощью которых кэшируют значения в Redis.</p>
12
</ul><p>Redis чаще всего используют именно как кэширующий сервер. Он имеет богатый встроенный функционал для этих целей. Рассмотрим основные команды, с помощью которых кэшируют значения в Redis.</p>
13
<h3>Кэширование</h3>
13
<h3>Кэширование</h3>
14
<p>Представим, что нужно записать количество сделок пользователя в кэш. Запишем, что у пользователя с ID<em>33</em>имеется<em>5</em>сделок. Используем обычную команду set:</p>
14
<p>Представим, что нужно записать количество сделок пользователя в кэш. Запишем, что у пользователя с ID<em>33</em>имеется<em>5</em>сделок. Используем обычную команду set:</p>
15
<p>Данные записаны. Однако они будут храниться вечно до следующей перезагрузки сервера или пока не будут удалены вручную. Одно из основных свойств кэша - это то, что он хранится короткий промежуток времени. Данных может быть много, а ресурсы серверов не бесконечны. К счастью в Redis можно указать время жизни ключа (время экспирации), по истечении которого ключ будет удален. Для этого достаточно добавить постфикс ex количество_секунд к команде set:</p>
15
<p>Данные записаны. Однако они будут храниться вечно до следующей перезагрузки сервера или пока не будут удалены вручную. Одно из основных свойств кэша - это то, что он хранится короткий промежуток времени. Данных может быть много, а ресурсы серверов не бесконечны. К счастью в Redis можно указать время жизни ключа (время экспирации), по истечении которого ключ будет удален. Для этого достаточно добавить постфикс ex количество_секунд к команде set:</p>
16
<p>Проверить, через сколько ключ удалится можно командой ttl:</p>
16
<p>Проверить, через сколько ключ удалится можно командой ttl:</p>
17
<p>В данном случае ключ<em>user:33</em></p>
17
<p>В данном случае ключ<em>user:33</em></p>
18
<p>исчезнет через 115 секунд.</p>
18
<p>исчезнет через 115 секунд.</p>
19
<p>Если попытаться получить значение спустя это время, то вернется пустой ответ:</p>
19
<p>Если попытаться получить значение спустя это время, то вернется пустой ответ:</p>
20
<p>Время жизни можно задавать не только в секундах, но и в миллисекундах с помощью префикса px количество_миллисекунд:</p>
20
<p>Время жизни можно задавать не только в секундах, но и в миллисекундах с помощью префикса px количество_миллисекунд:</p>
21
<p>Проверка времени жизни осуществляется командой pttl:</p>
21
<p>Проверка времени жизни осуществляется командой pttl:</p>
22
<p>Вывод выше показывает, что ключ<em>user:33</em></p>
22
<p>Вывод выше показывает, что ключ<em>user:33</em></p>
23
<p>исчезнет через 7484 миллисекунд (~7.4 секунд).</p>
23
<p>исчезнет через 7484 миллисекунд (~7.4 секунд).</p>
24
<p>Для установки времени жизни для уже существующего ключа используется команда expire</p>
24
<p>Для установки времени жизни для уже существующего ключа используется команда expire</p>
25
<h3>Инвалидация кэша</h3>
25
<h3>Инвалидация кэша</h3>
26
<p>Любой кэш может стать неактуальным раньше времени жизни. В нашем примере со сделками это может произойти при возникновении новой сделки у пользователя. В этом случае нужно удалить ключ в кэше, чтобы при следующем запросе произошел пересчет значения. Может возникнуть вопрос: а почему бы просто не добавить значение в имеющийся ключ? В данном примере это было бы наилучшим решением, но действовать необходимо не "в лоб". При увеличении значения двумя командами get + set может получиться неконсистентное состояние (подробнее эта проблема будет рассмотрена в следующем уроке). Также обновление значения не всегда возможно, например, в случае если значение - это среднее арифметическое.</p>
26
<p>Любой кэш может стать неактуальным раньше времени жизни. В нашем примере со сделками это может произойти при возникновении новой сделки у пользователя. В этом случае нужно удалить ключ в кэше, чтобы при следующем запросе произошел пересчет значения. Может возникнуть вопрос: а почему бы просто не добавить значение в имеющийся ключ? В данном примере это было бы наилучшим решением, но действовать необходимо не "в лоб". При увеличении значения двумя командами get + set может получиться неконсистентное состояние (подробнее эта проблема будет рассмотрена в следующем уроке). Также обновление значения не всегда возможно, например, в случае если значение - это среднее арифметическое.</p>
27
<p>Удаление ключа происходит командой del:</p>
27
<p>Удаление ключа происходит командой del:</p>
28
<p>Вернувшееся значение<em>1</em>означает, что ключ существовал и был успешно удален. Если ключа не было, то вернулся бы 0.</p>
28
<p>Вернувшееся значение<em>1</em>означает, что ключ существовал и был успешно удален. Если ключа не было, то вернулся бы 0.</p>
29
<p>Иногда требуется удалить тысячи ключей за раз. Например, у поставщика цен на акции произошел сбой и все сделки за период сбоя оказались недействительными. В этом случае нельзя использовать N вызовов команды del, потому что это будет выполняться очень долго и заберет все ресурсы Redis. Для множественного удаления существует команда unlink, которую можно безопасно использовать в продакшен-среде:</p>
29
<p>Иногда требуется удалить тысячи ключей за раз. Например, у поставщика цен на акции произошел сбой и все сделки за период сбоя оказались недействительными. В этом случае нельзя использовать N вызовов команды del, потому что это будет выполняться очень долго и заберет все ресурсы Redis. Для множественного удаления существует команда unlink, которую можно безопасно использовать в продакшен-среде:</p>
30
<p>Все ключи для удаления указываются через пробел. Стоит учитывать, что ключи удаляются асинхронно, то есть могут существовать после unlink короткий промежуток времени.</p>
30
<p>Все ключи для удаления указываются через пробел. Стоит учитывать, что ключи удаляются асинхронно, то есть могут существовать после unlink короткий промежуток времени.</p>
31
<h2>Резюме</h2>
31
<h2>Резюме</h2>
32
<ul><li>кэш ускоряет время ответа клиенту, но не заменяет надежных хранилищ</li>
32
<ul><li>кэш ускоряет время ответа клиенту, но не заменяет надежных хранилищ</li>
33
<li>в Redis ключ с временем жизни в секундах записывается командой set ключ значение ex количество_секунд, а в миллисекундах командой set ключ значение px количество_миллисекунд</li>
33
<li>в Redis ключ с временем жизни в секундах записывается командой set ключ значение ex количество_секунд, а в миллисекундах командой set ключ значение px количество_миллисекунд</li>
34
<li>инвалидация - неотъемлемая часть работы с кэшем. Чтобы удалить атомарный ключ, используется del ключ. Для удаления множества ключей существует команда unlink ключ1 ключ2 ...</li>
34
<li>инвалидация - неотъемлемая часть работы с кэшем. Чтобы удалить атомарный ключ, используется del ключ. Для удаления множества ключей существует команда unlink ключ1 ключ2 ...</li>
35
</ul>
35
</ul>