Зачем нужно кэширование
Перечислим основные задачи кэширования:
- увеличение скорости выполнения программ. Иногда программы состоят из функций, которые выполняются многократно или достаточно долго. Например, если они обрабатывают большой объем данных. Сохранение результатов этих вычислений в кэше обеспечивает быстрый доступ к ним и устраняет необходимость повторных вызовов, за счет чего программа выполняется быстрее;
- сокращение нагрузки на внешние источники информации. Чтобы получить необходимую информацию, приложения делают запросы к базам данных, API и другим источникам, а когда такие запросы многократно повторяются, производительность снижается. Кэширование позволяет приложениям быстро извлекать нужную информацию из кэша вместо постоянного обращения к внешним системам;
- улучшение пользовательского опыта. Здесь в первую очередь речь идет о веб-приложениях, которые должны реагировать на запросы пользователей с низкой задержкой. Если сохранять ранее запрашиваемую информацию в кэше, то приложение сможет получить к ней доступ быстрее, за счет чего опыт взаимодействия пользователей улучшится.
Встроенный декоратор
В Python существует встроенный инструмент для кэширования — декоратор functools.lru_cache. Помимо своей основной задачи, он автоматически удаляет из кэша те значения, которые не использовались дольше всего (аббревиатура LRU в названии расшифровывается как Least Recently Used), если кэш заполнен. lru_cache() не подходит для хранения больших объемов данных и использования в распределенных системах.
У lru_cache() есть два параметра:
- maxsize (значение по умолчанию 128) — определяет максимальный размер кэша, то есть результаты скольких вызовов функций будут сохранены. Когда кэш достигнет указанного размера, значения, которые давно использовались, начнут удаляться. Если установить maxsize=None, то все результаты будут сохраняться до тех пор, пока не закончится память — этим можно пользоваться, но стоит помнить, что риск снижения производительности в таком случае повышается;
- typed (значение по умолчанию False) — если установлено значение True, то результаты вызова функции с аргументами разных типов (например, f(3) и f(3.0)) будут сохраняться как отдельные записи. Если установлено значение False, то результаты вызова функции с аргументами разных типов будут сохранены в одну запись.
Небольшой пример использования декоратора:
from functools import lru_cache
@lru_cache(maxsize=32) # Максимальный размер кэша — 32, по умолчанию typed=False
def multiply_by_two(value):
return value * 2
multiply_by_two(5) # Будет записано в кэш
multiply_by_two(5.0) # Вернется значение из кэша, так как typed=False
Если изменить значение typed на True, то указанные вызовы функции будут сохранены в разные записи кэша.
Рассмотрим несколько полезных методов.
- cache_info() для просмотра статистики кэша:
some_function.cache_info()
- cache_clear() для очистки кэша:
some_function.cache_clear()
- cache_parameters() для просмотра параметров (maxsize и typed):
some_function.cache_parametres()
Кэширование с использованием внешних хранилищ
Внешние хранилища позволяют обойти ограничения встроенного в Python-декоратора: они подходят для использования в распределенных системах и могут хранить большие объемы данных.
Рассмотрим два популярных инструмента:
- Redis — это NoSQL СУБД, которая хранит данные в формате «ключ-значение» и в том числе используется для кэширования. Установить Redis можно через стандартный для Python менеджер пакетов pip:
pip install redis
Пример использования Redis:
import redis
# Подключение
cache = redis.Redis(host='localhost', port=6379, db=0)
def multiply_by_two(value):
# get() используется для получения данных из кэша
cached_result = cache.get(value)
# Проверка value в кэше
if cached_result:
return int(cached_result)
# Если значения в кэше нет, то оно будет сохранено
else:
result = value * 2
# Первый аргумент setex — это ключ, по которому значение можно получить/удалить
# Второй — время, на которое сохраняется значение в секундах
# Третий — значение, которое сохраняется
cache.setex(value, 60, result)
return result
# Синтаксис для удаления одного ключа
cache.delete(key)
- Memcached — это похожий на Redis инструмент, который тоже хранит информацию в формате «ключ-значение». Однако скорее подходит для сохранения промежуточных данных или другой информации, которую не нужно хранить долго. Установить Memcached можно следующим образом:
pip install pymemcache
Пример использования Memcached схож с Redis:
from pymemcache.client import base
# Подключение
cache = base.Client(('localhost', 11211))
def multiply_by_two(value):
cached_result = cache.get(value)
if cached_result:
return int(cached_result)
else:
result = value * 2
cache.set(value, result, expire=60)
return result
Заключение
- Смысл кэширования сводится к сохранению результатов вызовов сложных или многократно используемых функций для быстрого доступа к ним и снижения нагрузки на систему.
- В Python есть встроенный декоратор functools.lru_cache, однако он не подходит для хранения большого количества значений и использования в распределенных системах.
- Внешние хранилища, такие как Redis и Memcached, позволяют разработчикам обойти ограничения lru_cache.