Создайте фреймворк кэширования в Python

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

Преимущества

Кэш читает / записывает данные из памяти, что обычно в 1000 раз быстрее, чем средний диск. Как внутрипроцессный, так и распределенный кеш хранит данные в сжатом двоичном формате и помогает существенно сократить время отклика приложения, в то время как использование памяти сознательно оптимизируется.

Общие проблемы

Кэширование - это обычная функция в дизайне приложения, хотя, как правило, приложения кэшируют статические данные, чтобы избежать обхода БД, но не планируют делать его гибким и обрабатывать варианты использования DML:

  1. Обработка манипуляций с данными, удаление и перезагрузка данных при каждом изменении нецелесообразно.
  2. Выполняя стандартные операции фильтрации, сортировки и разбивки на страницы, большинство клиентов кеширования не поддерживают настраиваемые операции из коробки.

План проектирования каркаса

  1. Определите набор констант для принятия решения в структуре кэширования. Константы уменьшают жестко закодированные значения и являются единственным источником модификации
  2. Инициализируйте локальный и распределенный кеш, мы будем использовать dict для локального кеша и redis для распределенного
  3. Создайте операцию генерации случайных данных для миллиона записей с несколькими типами данных. Имитация операции чтения из базы данных
  4. Создайте сериализованный ключ кеша json на основе параметров сортированного метода
  5. Получение / установка данных из кеша потокобезопасным способом, избегая двустороннего обхода БД
  6. Добавить данные в первый раз в кеш для использования в будущем
  7. Двоичная сериализация (рассол) и сжатие данных, особенно для распределенного кеша
  8. Запустить операцию фильтрации и сортировки кэшированных данных
  9. Запустите операции DML с кешем для согласования с изменениями базы данных.

Константы кеша

Константы используются в проекте для уменьшения жестко заданных значений.

Requirements.txt

Бинарные файлы для фреймворка кеширования должны быть установлены с помощью установщика pip. Мы используем Python 3.8.2

Инициализировать клиент кеша

Инициализируйте как локальный, так и распределенный кеш. Локальный кеш - dict, для Redis мы используем Python Redis Client. Локальный кеш можно заменить более прочным functools-LruCache. Переменная среды cache_type, используется для определения того, какой кеш использовать, по умолчанию в кеше процесса.

Генерация случайных данных

Сгенерируйте 1 миллион записей, используя методы генерации случайных данных. Этот метод имитирует работу с базой данных. Он использует случайный пакет python

CacheGetSize

Вспомогательный метод для измерения размера кешируемых объектов

Функции кеширования

Создать ключ кэша, взять словарь ключей статического кеша и словарь отсортированных первичных ключей, которые затем объединяются и сериализуются в формате json для создания ключа кеширования строк. Ключи сортируются, чтобы гарантировать ограниченную комбинацию и отсутствие перестановок, и используются только первичные ключи (что тоже необязательно), чтобы избежать пересечения данных между дампами кеша

Получение и установка данных кэша, потокобезопасные операции. Операция Get проверяет ключ кеша json и, если доступно, извлекает данные. При необходимости он выполняет распаковку и распаковку данных перед выполнением логических операций. Set - это однократная операция после загрузки данных из БД. Для распределенного кеширования необходимо травление, мы можем даже сжать, чтобы уменьшить задержку в сети, но каждый раз распаковка требует дополнительных затрат.

Фильтрация кеша

Примените фильтр к кэшированным данным на основе метода FetchFilters. Фильтрация может быть многопроцессорной или линейной в зависимости от размера данных. DataFilterProcessing - это метод фильтрации данных. Общие типы данных числовые, строковые, логические, datetime и сложные типы list и Json обрабатываются эффективно. Охватывает широкий спектр вариантов использования, включая сопоставление с образцом с использованием регулярного выражения.

Сортировка кеша

Ограниченный вариант использования, выполняется с использованием первичного ключа, если выполняется фильтрация с несколькими обработками, в противном случае данные могут быть предварительно отсортированы и сохранены. Можно также использовать сортировку по другим столбцам, но, как правило, следует избегать сортировки больших данных, это трудоемкая операция. Мы также можем разбивать на страницы, чтобы отправлять вызывающему абоненту только ограниченные данные. Пагинация может быть сделана с помощью Pylinq или понимания списка.

Кэшировать DML, обновить, вставить и удалить

Кэшированные данные загружаются в память, и мы можем запускать операции DML для согласования с модификациями базы данных. Все операции DML основаны на первичном ключе. Обычно сначала обновляется база данных, затем кеш, их нельзя связать с помощью транзакции, если операция с кешем завершается неудачно, тогда в блоке исключения очистите ключ кеширования и перезагрузите из базы данных в следующем вызове чтения (идея состоит в том, чтобы синхронизировать те же изменения, где db - это основной источник)

Плести все вместе

Весь вышеперечисленный код собран в следующем репозитории Github. Выполните python CacheMain.py для запуска различных тестовых случаев, по умолчанию он использует локальный кеш, установите cache_type, переменную среды на распределенный для использования Redis, в котором сервер должен работать на основе строки подключения, текущая программа ожидает localhost: 6379

Заключение

  1. Для распределенного кэша обрабатываются данные, которые могут быть дополнительно сжаты с использованием алгоритмов, упомянутых в файле констант.
  2. В нашем тесте 1 миллион записей составляет около 360 МБ данных, которые уменьшаются до 27 МБ при травлении и далее до 2 МБ при сжатии с использованием Bz2. Сжатие - это компромисс, так как оно экономит время в сети, поскольку время декомпрессии добавляется каждый раз, что делает его медленнее, чем простая обработка
  3. Даже данные локального кеша могут быть сохранены в маринованном формате, так как это помогает экономить оперативную память, но тогда их нужно каждый раз распаковывать.
  4. Для локального кеша, если данные не обрабатываются, мы будем создавать глубокую копию каждый раз, когда мы будем совместно использовать ссылку на объект данных по запросам, что может привести к повреждению. По умолчанию dict не является потокобезопасным.
  5. Для локального кеша мы используем блокировку Python, а для Redis - блокировку клавиш Redis в Python, чтобы операции выполнялись атомарно.
  6. Локальный кеш находится в процессе, и, хотя он снижает задержку в сети, но имеет ограничения, связанные с тем, что он привязан к процессу, может масштабироваться только по вертикали и исчезнет вместе с процессом. Для корпоративных разработок распределенный кеш, такой как Redis, является гораздо более прочным и надежным вариантом.

Ресурсы

Репозиторий Github, https://github.com/mrinalkamboj/PythonCaching.git