Что такое redis python
Redis для начинающих
Введение
С одной стороны есть традиционные ACID реляционные базы данных такие как MySQL, PostgreSQL, Oracle и др. Они надежны и стабильны. Сама аббревиатура ACID описывает требования к транзакционной системе (Atomicity — Атомарность, Consistency — Согласованность, Isolation — Изолированность, Durability — Долговечность). Их основная задаче не просто хранить данные, а хранить с максимальной надежностью. Но их основной недостаток, они очень медленные.
С другой стороны есть очень быстрые хранилища в памяти типа ключ-значение, например memcached. Они очень быстрые за счет предельной простоты и отказа от надежности. Несложно привести несколько примеров задач где нужна производительность даже за счет надежности:
— система мониторинга с n-ым количеством датчиков, которые должны постоянно отправлять данные.
— система логирования действий пользователя на каком нибудь сайте
— контроль трафика сети в реалтайм
— хранение сессий пользователей
и т.д.
Redis относится к третьему типу хранилищей. Когда нужно быстрая обработка данных и при этом сохранялось бы определенный уровень надежности и возможности масштабирования.
Redis (REmote DIctionary Server) — это не реляционная структура данных в памяти, используемая в качестве базы данных. Данные хранятся в виде пары ключ-значение. И при этом хранилище умеет масштабироваться путем репликации между серверами. Redis сохраняет все данные в памяти, что позволяет сделать доступ к данным максимально быстрым по сравнению с другими базами данных. Почему Redis известен своей исключительной высокой производительностью даже среди других key-value хранилищ.
Redis позволяет нам хранить данные в высокоуровневых структурах данных, такие как строки, хэши, списки, наборы. Это дает нам больше гибкости в отношении типа и объема информации, которую мы можем хранить в хранилище данных Redis.
Он также довольно дружелюбен для разработчиков, поскольку поддерживает большинство языков высокого уровня, таких как Python, JavaScript, Java, C / C ++ и PHP.
Установка Redis
Для дальнейшего изучения Redis нам необходимо скачать и установить сервер Redis. Можно использовать инструкции с официальной веб-страницы. Так же под MacOS можно использовать Homebrew, а для Linux что типа apt. Для запуска Redis используется команда redis-server
У Redis есть Redis-CLI (Common Line Interface), который можно использовать для взаимодействия с данными напрямую на сервере Redis.
Чтобы проверить правильность установки Redis, запустите redis-cli, а затем введите команду ping в появившейся подсказке:
Если сервер отвечает ответом PONG значит он готов к работе. По умолчанию сервер Redis работает на порту 6379, что видно в нашем приглашении.
Что бы выйти из redis-cli используйте команду quit.
Для вывода помощи по списку команд в консоли можно использовать команду HELP @string. Для вывода помощи по конкретной команде можно использовать команду HELP APPEND Например:
Далее приведен краткий список самых необходимых команд. Для изучения полного списка команд, если в этом есть необходимость, обратитесь к официальной документации.
Прежде чем начать использовать Redis в любом языке программирования нужно узнать о базовых командах и структурах используемых в Redis.
Команды
Прежде всего Redis это хранилище типа ключ: значение. И самые первые команды которую все изучают, это команды SET и GET:
Команда используется для установки ключа и его значения, с дополнительными необязательными параметрами для указания срока действия записи значения ключа. Давайте установим ключ foo со значением «hello world». Параметр EX указывает время жизни объекта в секундах, PX в милисекундах:
Команда используется для получения значения, связанного с ключом. Если запись значения ключа превысила срок действия, будет возвращено nil:
По умолчанию все значение в Redis сохраняются как строки.
EXISTS
Эта команда проверяет, существует ли что то с данным ключом. Она возвращает 1 если объект существует или 0 если нет. Boolean типа в Redis нет.
FLUSHALL
Эта команда полностью удаляет все данные в текущем сеансе.
GETSET
Команда возвращает текущее значение и устанавливает новое. Используется для атомарного управления данными.
Команда удаляет ключ и соответствующее значение:
APPEND
Команда добавляем в соотвествующий ключ дополнительное значение. Возвращает количество символов итогового значения.
Возвращает все ключи из базы по указанному шаблону. Есть предостережение что в реальных приложения эту команду лучше не использовать из-за того что она очень медленная.
INCR / DECR
Инкремент / декримент. Если значение ключа integer (хотя в базе храниться все равно строка) можно увеличить или уменьшить значение на 1. Если использовать команду INCR с несуществующем значением то создаться новый ключ со значением 1.
Когда ключ установлен с истечением срока действия (например SET foo EX 10), эту команду можно использовать для просмотра оставшегося времени:
PERSIST
Если мы передумаем об истечении срока действия ключа, мы можем использовать эту команду, чтобы удалить период истечения срока действия:
RENAME
Эта команда используется для переименования ключей на нашем сервере Redis:
Комплексные типы данных
Хеш таблицы
Redis позволяет в качестве значения так же использовать ключ: значение. Что по сути будет почти аналогией объектов из JavaScript или словари в Python. Для записи объекта используется команда HSET в следующем формате HSET имя_ключа имя_атрибута значение. Для чтения объекта используется команда HGET в формате HGET имя_ключа имя_атрибута. Команда HGETALL используется для получения
Множества
Не упорядоченная коллекция уникальных элементов. Аналог set в Python. Для добавление нового элемента во множество используется команда SADD. Для получения все элементов используется команда SMEMBERS. SUNION используется для объединение множеств. SDIFF используется для вычитания из первого множества второго. SINTER возвращает общие элементы указаных множеств. SPOP удаляет и возвращает случайный элемент множества.
Упорядоченные множества
Упорядоченная коллекция уникальных элементов. Для добавление нового элемента в упорядоченное множество используется команда ZADD. Формат ZADD имя_ключа порядковое_число_упорядочивания_множества значение
Команда ZRANGE возвращает срез данных множества
Списки
Транзакции в Redis
Обычное определение транзакций для реляционных баз данных означает следующее: транзакции это группа команд с базой данных, которые должны либо полностью выполнится или в случае возникновение ошибки вернуть состояние базы данных в исходное состояние. В Redis то же есть такое понятие как транзакции. Но означает немного другое. Транзакции в Redis это просто последовательное выполнение ранее записаных команд без возможности полноценного возвращения исходного состояния в случае ошибки исполнения.
С помощью команды MULTI можно начать запись команд. Далее введенные команды не исполняются а записываются в буфер. Это будет происходит до ввода команды на исполнения транзакции EXEC. Далее все ранее введенные команды будут исполнены один за другим. Команда DISCARD отмена записи команд транзакций. Если возникнет ошибка в процессе ввода команд вся транзакция не будет выполнена.
Механиз подписок PUS-SUB
Одно из основных преимуществ Redis от других key-value хранилищ заключается в том, что в Redis есть механизм подписок. То есть Redis можно использовать как сервер сообщений.
Одни клиенты подписываются на определенные каналы используя команду SUBSCRIBE имя_канала
Другие клиенты могут отправлять сообщения в этот канал используя команду PUBLISH имя_канала значение
Допустим один клиент подписывается на канал
Другой клиент что то отправляет в этот канал
И в этот момент первый клиент получит это сообщение
Основы применение Redis в Python
Redis очень широко применяется в современной разработке ПО. Библиотеки поддержки есть для любого языка программирования.
Кратко рассмотрим использование Redis в Python. Для этого первым делом загрузим библиотеку поддержки:
Далее подключимся к серверу
И далее можно уже начать попробовать использовать все ранее рассмотренные команды. Надеюсь они будут понятны без дополнительных пояснений:
Заключение
Redis — это мощный и быстрый вариант хранения данных, который при правильном использовании может принести много преимуществ. Он не имеет крутой кривой обучения, поэтому с ним легко начать работать. Также поставляется с удобным инструментом CLI, который помогает нам взаимодействовать с ним с помощью простых и интуитивно понятных команд.
Как работать с базой данных Redis в Python
Чтобы использовать Redis в наших Python-проектах, первое, что нам нужно сделать, это установить его. Для этого мы запускаем следующую команду:
В этой статье я буду использовать Docker и контейнер Redis, но вы можете установить его на свою систему. Если вы хотите посмотреть более подробную информацию о том, как установить Redis, я оставляю эту ссылку.
Подключение к Redis с помощью Python
Если мы хотим подключиться к Redis из Python, нам просто нужно вставить следующую строку в наш код:
В нем вы должны будете заменить хост, порт и db на свои.
В случае если ваша база данных использует пароль или вы хотите добавить дополнительную конфигурацию, вы должны установить соединение с помощью метода StrictRedis:
Создание и извлечение данных
Если мы хотим установить значение, нам просто нужно использовать метод set с ключом и значением:
Чтобы получить его, мы воспользуемся методом get, передав ключ в качестве параметра:
Этот метод возвращает значение ключа в байтовом формате. Если мы хотим преобразовать его в строку, мы можем использовать функцию decode:
Добавление времени жизни данным
В случае, если мы хотим установить время жизни для наших ключей, мы можем использовать параметр ex, чтобы добавить, сколько секунд мы хотим, чтобы они существовали, или px, если мы хотим добавить время в миллисекундах:
В этом случае ключ foo будет иметь время жизни 10 секунд.
Чтобы узнать, сколько времени осталось жить, мы можем использовать методы ttl для отображения в секундах и pttl для отображения в миллисекундах:
Если мы передумаем и решим снять ограничение по времени, мы воспользуемся методом persist:
Если теперь мы захотим добавить время жизни к этому ключу, мы воспользуемся методом setex, в котором нам нужно будет передать ключ, время жизни и значение, которое мы хотим ему присвоить:
Если мы хотим передать его в миллисекундах, то воспользуемся методом psetex.
Атомарные операции
Атомарная операция в Redis – это процесс, который всегда выполняется полностью, а если это не так, то он может быть отменен.
В случае Redis мы можем использовать их для создания увеличивающихся и уменьшающихся значений для наших ключей. Для того чтобы их использовать, создадим ключ с числовым значением, а затем запустим функцию, которая будет увеличивать и уменьшать значение этого ключа:
Если мы хотим увеличить значение, мы используем метод incr, в противном случае мы используем метод decr.
Если мы хотим увеличить или уменьшить значение на заданное нами число, а не по одному, мы будем использовать методы incrby и decrby, в которых мы передадим ключ и значение:
Заключение
Как видите, переход от Redis в консоли на язык Python довольно прост. Методы вызываются так же, как и команды, можно только задаться вопросом, как передать определенные параметры, но для этого есть документация.
Похожие записи
В этой статье мы разберем что такое Redis и как начать с ней работать. Что…
Некоторое время назад у меня возникла ошибка при вызове https-адреса в pyhon. После долгих поисков…
Сравнивать даты в Python очень просто. Для этого достаточно использовать операторы сравнения. В этой статье…
Python: работа с Redis в Django
Данные становятся все более ценным товаром в современную эпоху технологий, и это требует оптимизации хранения и доступа к этим данным.
Существует довольно много известных решений для хранения данных, в том числе реляционные системы управления базами данных (RDBMS), такие как MySQL и PostgreSQL, которые хранят данные в структурированном формате с использованием строк и столбцов и связей внутри данных.
Помимо СУБД, существуют key/value хранилища, в которых хранятся данные на основе уникальных ключей и значений, например словарь. Базы данных ключ-значение относятся к семейству баз данных NoSQL, которые не соответствуют реляционной природе СУБД.
В этой статье мы рассмотрим Redis как хранилище ключей и используем его в проекте для изучения его функциональности.
Что такое Redis и зачем его использовать?
Данные хранятся в Redis в форме ключ/значение, где ключи используются для поиска и извлечения данных, хранящихся в экземпляре Redis.
Обычные базы данных хранят данные на диске, что влечет за собой дополнительные затраты с точки зрения времени и аппаратных ресурсов. Redis избегает этого, сохраняя все данные в памяти, что делает данные легкодоступными и увеличивает скорость доступа к данным и манипулирования ими по сравнению с обычными базами данных.
Это причина, почему Redis известен своей исключительной высокой производительностью.
Redis позволяет нам хранить данные в нескольких высокоуровневых структурах данных, включая строки, хэши, списки, наборы и отсортированные наборы. Это дает нам больше гибкости в отношении типа и объема информации, которую мы можем хранить в хранилище данных Redis.
Написанный на ANSI C, Redis легок и не имеет внешних зависимостей. Он также довольно дружественен для разработчиков, поскольку поддерживает большинство языков высокого уровня, таких как Python, JavaScript, Java, C/C ++ и PHP.
Когда следует использовать Redis?
Типичные случаи использования Redis:
Такие компании, как Twitter, Pinterest, Github, Snapchat и StackOverflow, используют Redis для хранения и обеспечения высокой доступности данных для своих пользователей.
Например, Twitter хранит самые последние входящие твиты для пользователя в Redis, чтобы ускорить доставку твитов в клиентские приложения.
Pinterest использует Redis для хранения списка пользователей и форумов, за которым следит пользователь, списка подписчиков пользователя, а также списка людей, которые следят за вашими досками, среди других списков, чтобы улучшить взаимодействие с платформой.
Установка Redis
Для дальнейшего изучения Redis нам необходимо скачать и установить сервер Redis, используя инструкции с официальной веб-страницы. Redis также доступен в виде образа Docker на Docker Hub.
Он также поставляется с инструментом Redis-CLI, который мы можем использовать для взаимодействия с данными на нашем сервере Redis и управления ими.
Redis также доступен для установки через Homebrew (для MacOS) и через репозиторий apt по умолчанию для Debian Linux и его вариантов, таких как Ubuntu.
Чтобы установить Redis на MacOS, просто запустите:
Redis Commands
Redis через Redis-CLI предоставляет некоторые удобные команды, которые мы можем использовать для взаимодействия с сервером Redis и манипулирования данными, хранящимися там. По умолчанию серверы Redis работают на порту 6379, и это будет видно в нашем приветственно сообщении при старте сервера.
Команды, доступные в Redis-CLI, включают в себя:
SET : Эта команда используется для установки ключа и его значения, с дополнительными необязательными параметрами для указания срока действия записи значения ключа. Давайте установим ключ hello со значением world с истечением через 10 секунд:
GET : Эта команда используется для получения значения, связанного с ключом. Если запись значения ключа превысила срок действия, будет возвращено nil :
DELETE : Эта команда удаляет ключ и соответствующее значение:
TTL : Когда ключ установлен с истечением срока действия, эту команду можно использовать для просмотра оставшегося времени:
PERSIST : Если мы передумаем об истечении срока действия ключа, мы можем использовать эту команду, чтобы удалить период истечения:
RENAME : Эта команда используется для переименования ключей на нашем сервере Redis:
FLUSHALL : Эта команда используется для очистки всех записей значения ключа, которые мы установили в нашем текущем сеансе:
Более подробную информацию об этих и других командах Redis можно найти на официальном сайте.
Redis с Django
Чтобы продемонстрировать, как интегрировать Redis в веб-приложение, мы создадим API с использованием Django и Django REST, который сможет получать пару ключ-значение и сохранять ее на нашем сервере Redis.
Наш API также сможет получать значения для заданных ключей, извлекать все сохраненные пары ключ-значение и также удалять запись ключ-значение.
Давайте начнем с создания папки для размещения нашего проекта:
Затем давайте создадим виртуальную среду и активируем ее:
И наконец, давайте установим необходимые библиотеки:
Давайте теперь создадим приложение:
Чтобы убедиться, что наша установка Django прошла успешно, мы запускаем сервер:
С помощью этого параметра мы также можем использовать экземпляр Redis, запущенный внутри контейнера Docker, или внешний экземпляр Redis, хотя нам может потребоваться предоставить данные для аутентификации для этого случая. Сейчас мы будем использовать наш локальный экземпляр Redis, который мы только что настроили.
Наши представления будут простыми представлениями на основе функций, которые позволят нам взаимодействовать с сервером Redis. Во-первых, давайте создадим URL-адреса, с которыми мы будем взаимодействовать в нашем api/urls.py :
Первый путь позволит нам создавать записи и просматривать все записи, а второй путь даст нам детальное управление отдельными записями.
Чтобы лучше объяснить код, мы разберем его на более сжатые куски. Если вы хотите увидеть полный код, в заключении этой статьи есть ссылка на репозиторий GitHub с исходным кодом.
Мы начнем с импорта необходимых библиотек и подключения к нашему экземпляру Redis:
Теперь давайте создадим manage_item() :
manage_item() дает нам доступ к отдельным записям в нашем экземпляре Redis. Это представление требует, чтобы вызывающая сторона передала ключ элемента, который нам нужен в URL.
Этот ключ затем используется для поиска значения, хранящегося в нашем экземпляре. Используя HTTP метод PUT и передавая новое значение ключа, мы можем обновить значение ключа.
С помощью метода DELETE мы можем удалить пару ключ-значение из нашего экземпляра Redis.
Чтобы увидеть наш API в действии, мы будем использовать Postman. Но сначала давайте создадим одну или две записи с помощью инструмента redis-cli :
После настройки данных отправим запрос GET по адресу localhost:8000/api/items :
Наш API способен извлекать все пары ключ-значение в нашем текущем экземпляре Redis. Теперь давайте отправим запрос POST со следующим содержимым на тот же URL:
Давайте отправим еще один запрос GET на тот же URL:
Мы видим, что ключ, который мы создали с помощью нашего API, сохраняется в нашем экземпляре Redis. Мы можем проверить его существование с помощью инструмента CLI.
Теперь давайте проверим второй URL, который возвращает значение одного ключа, отправив запрос GET по адресу http://localhost:8000/api/items/HELLO :
Когда мы снова получим ключ HELLO :
Когда мы пытаемся получить доступ к тому же элементу после его удаления:
Вывод
Мы смогли без проблем интегрировать наш Django API с локально работающим экземпляром Redis, что свидетельствует о его простоте использования с распространенными языками программирования высокого уровня.
Redis Python based cluster. Часть 1: распределённые системы, теоремы CAP и PACELC и зачем нужен Redis
Рано или поздно сервисы растут, а с большим RPS приходит Highload.
Что делать, когда ресурсов для вертикального масштабирования Redis уже нет, а данных меньше не становится? Как решить эту задачу без downtime и стоит ли её решать с помощью redis-cluster?
На воркшопе Redis Python based cluster Савва Демиденко и Илья Сильченков пробежались по теории алгоритмов консенсуса и попробовали в реальном времени показать, как можно решить проблему с данными, воспользовавшись sharding’ом, который уже входит в redis-cluster.
Воркшоп растянулся на два часа. Внутри этого поста — сокращённая расшифровка самых важных мыслей.
Введение
Немного о тех, кто провёл воркшоп, и почему вообще его решили провести.
Савва Демиденко
Занимаюсь разработкой в Avito, делаю программу курса «Мидл Python-разработчик» от Яндекс.Практикума. Закончил Бауманку и Технопарк. Разрабатываю на Python и Golang. Люблю решать архитектурные задачи в веб-программировании.
Илья Сильченков
Тимлид в «Сбермаркете» и наставник на курсе «Мидл Python-разработчик». Успел побыть фронтендером и дата-инженером, но остановился на бэкенде. Сейчас пишу на Python и Go.
В рамках нашего курса в «Яндекс.Практикуме» в течение шести месяцев мы делаем онлайн-кинотеатр из множества микросервисов. Сначала пишем маленькую ETL из Elasticsearch и Flask, потом — админку и асинхронное API, авторизацию/аутентификацию и систему уведомлений. В том числе есть маленькая продуктовая задача — пиар в социальных сетях.
Как известно, у Twitter есть смешное ограничение на количество символов. Часто пользователи сокращают ссылки. Один из сервисов нашего курса вставляет ссылку в простенький API, а тот её сокращает и отдаёт обратно.
Для решения этой задачи мы взяли простой стек с прицелом на Python-разработку: FastAPI, асинхронный фреймворк pydantic для валидации и Redis в качестве хранилища данных. Redis суперпростой, однопоточный и отдаёт данные за константное время. Кажется, чтобы сохранить ссылку и достать её, большего и не надо.
К сожалению, с большим RPS приходит highload — то состояние инфраструктуры, которое требует, чтобы ее оптимизировали и масштабировали. Сегодня мы будем решать задачу, когда наше хранилище, а именно Redis, больше не вмещается на одну серверную железку, поэтому нужно придумать, как разъезжаться.
Переезд на другое хранилище — дорогостоящая операция. Почитайте статьи, как Uber переезжал с MySQL на PostgreSQL — там всё ужасно. Речь про события 2013 года: из-за отрицательных результатов через три года в инфраструктуре Uber провели обратный переход. Смена хранилища под нашим сервисом — это последнее, к чему можно прийти.
Перед этим важно понять, как мы расходуем ресурсы, и попробовать оптимизировать хранение данных — перенести какие-то данные в холодное хранилище или удалить. Возможно, мы забывали чистить за собой память, и она засорилась. После всех оптимизаций нужно двигаться в сторону масштабирования.
В итоге хранилище превращается в несколько Redis-машин. До начала работы с практической задачей нужно разобраться с академической стороной вопроса.
Теория
Распределённые системы
Посмотрим, что такое распределённые системы с точки зрения computer science. Это важно понять, потому что от вас ожидают готовое решение, которое будет работать «из коробки». Для этого нужно не наступать на чужие грабли, а теория как раз позволяет их обойти.
Самое простое определение распределённой системы предлагает Лесли Лампорт, создатель алгоритма Паксос. Распределённая система — система, в которой отказ машины, о которой вы даже не подозревали, может превратить ваш компьютер в тыкву. При этом неважно, какая проблема с системой в этом виновата: сеть, электричество или ретроградный Меркурий.
Из этого определения следует, что любой веб-сайт — это распределённая система. В схеме его работы есть устройство клиента и хост-машина, на которую оно ходит. Если хост упадёт, то сайт работать не будет.
Неужели любая система, в которой участвует больше двух железок, распределённая? Почти.
Разберём на примере Redis Cluster. В нём появляются подвиды распределённых систем, для классификации которых потребуется теорема CAP. В ней описывается главное свойство распределённых систем — консистентность.
Википедия превращает консистентность в три отдельных понятия: «…согласованность данных друг с другом, целостность данных, а также внутренняя непротиворечивость».
Рассмотрим пример с обычной реляционной базой данных социальной сети. В ней есть таблица с пользователями и отдельно таблица с друзьями (парами ID). В этом случае целостность означает, что в таблице с парами связей не будет айдишек, которых нет в таблице с пользователями. А если пользователь удалится, то пары с ним либо тоже удалятся, либо не будут учитываться.
Связность означает, что если первый пользователь находится у второго в друзьях, то и второй находится в друзьях у первого. Пары связей согласуются друг с другом. Правило непротиворечивости требует, что если один пользователь удалит другого, то вся связь также будет удалена.
На деле консистентность бывает разной. То, что этом большом дереве с Jepsen.io выделено зелёным — это какой-то вид нормальной консистентности, а всё под ним — технические детали, которые к этому приближают.
Теорема САР
В теореме CAP используется линеаризуемая консистентность — linearazable. Приведём пример, когда это свойство отсутствует.
Допустим, что в распределённой системе есть три ноды: leader и две follower. Судья сказал, что Германия победила в футбольном матче. Insert произошёл на leader, на что тот ответил успехом записи, но раскатал изменения не на всех follower сразу. Алиса зашла и увидела победу Германии, а Боб видит, что матч ещё продолжается — изменение дошло не до всех follower. Если бы в этой системе консистентность была линеаризуемая, то оба участника прочитали бы про победу Германии. Для linearazable-консистентности важно, чтобы лидер ноды отвечал успехом уже после раскатывания на все ноды.
Второе свойство — partition tolerance, или устойчивость к разделению сети или сетевой нестабильности. Например, есть два узла, связанные кабелем. Если его перерубить, то произойдёт то самое разделение сети. Останется два контура со множеством серверов в каждом. Каждой ноде придётся самостоятельно решать, как обеспечивать два другие свойства теоремы CAP. Устойчивость означает, что система знает, как работать в такой ситуации.
Последнее свойство — доступность, или availability. Система ответит быстро, но без гарантии, что быстро и свежими данными. Линейная консистентность не ожидается.
Теорема CAP утверждает принцип тройственной ограниченности: можно получить только два свойства из трёх. Системы делятся на CP, CA и AP. Все системы пытаются покрыть сразу два свойства, а не одно.
Рассмотрим самые простые примеры. В PostgreSQL в синхронной репликации leader и follower связаны жёстко, то есть leader закроет транзакцию только после получения ответа от follower. Если кабель между ними разорвётся, то перестанут работать оба. В теореме CAP такой схеме работы соответствует консистентность и доступность (CA).
В случае асинхронной репликации изначально предусмотрен gap, и может случиться тот самый случай с футбольным матчем. При проблемах с сетью реплики продолжат отвечать в предусмотренное окно. При этом данные будут не самыми свежими. Такая система называется AP: она устойчива к разделению сети и обеспечивает доступность, но не консистентность.
Получается, каждый раз при решении задачи нужно посмотреть в теорему CAP, чтобы выбрать нужные свойства.
Теорема PACELC
PACELC пришла на смену теореме CAP и расширяет её. В ней получается четыре вида систем.
В правом верхнем углу — системы PC/EC, которые всегда выбирают консистентность. Это банки, самолёты и другие надёжные системы.
Кому это не нужно, идут в левый нижний угол — в системы PA/EL. При нарушении сетевой связности (partition) обеспечивается доступность, а в противном случае — скорость ответа (latency). Пример подобной системы — Amazon в «чёрную пятницу». Компания готова возвращать деньги покупателям, одаривать их купонами, дозаказывать товар, но главное — чтобы в этот день клиенты всё заказали. Неважно, будет ли всё в наличии, важно, чтобы можно было оформить заказ.
В левом верхнем углу — системы PA/EC. В случае разбиения сети нужна доступность, в другом случае — консистентность. По данному принципу работает MongoDB, хотя иногда она ближе к системе PC/EC. Если происходит разбиение, возможны нарушения консистентности.
Единственная известная система, которая работает по PC/EL, — поисковик Yahoo. При разбиении нод он выбирает консистентность, потому что ему важно сохранить данные и отвечать единообразно. При отсутствии проблемы он выбирает latency, чтобы быстро давать ответ. Когда всё работает хорошо, поисковики могут игнорировать небольшую неконсистентность уровня секунд и минут.
При чём тут Redis
Redis нельзя рассматривать в теореме CAP. Всё же это однонодовая штука, а теорема CAP — распределённая система хранения данных. Можно рассматривать это как распределённую систему с навешенным поверх сайтом: много нод и кода ходят в один Redis. И получится система CA: если между сайтом и Redis не будет сети, всё упадёт, но при этом каждая нода всё равно будет доступна.
Многие используют Redis в качестве кэширующего слоя. Реже в этой роли выступает Memcached. Но он живёт только в памяти, а комьюнити Redis отлично развивается.
В курсе «Мидл Python-разработчик» мы используем Redis Cluster в асинхронном API. Мы уже разбираем механизмы работы с нодами и как правильно их масштабировать.
Тема непростая, но несколько докладов от Amazon разъясняют, как всё нужно делать, — их вы найдёте в конце поста. Мы далеко не первопроходцы — R&D департамент Amazon это сделал уже в 2007 году.
Итак, в теореме CAP Redis Cluster — это P. Почему только P, без ещё одной буквы?
Обычно так случается, когда кто-то не до конца разобрался с темой и решил разработать собственное решение. Ребята из Jepsen.io провели анализ и сказали, что система устойчива к разделению. При этом она пытается вести себя как кластер. И если половина нод недоступна, работать она уже не может. Это говорит, что Redis Cluster — не система AP.
Остаётся одна P. Если мы используем WAIT, который приделали позже, когда дочитали теорию, Redis Cluster станет CP.
Острова и мосты
Разработка распределённых систем и проверка консистентности — хорошая инженерная задача. Во время анализа нужна какая-то аналогия для ума человека. Задача об обедающих философах — хороший тому пример. Здесь будет аналогия в виде островов и мостов между ними.
Острова живут как одно государство с общим сводом законов. И каждый остров хочет влиять на эти законы. Но не съездом в одном замке с разносом коронавируса, а пересылкой гонцов, как курьеров с едой. И перемещаться между островами они будут по мостам.
На каждом острове мы будем хранить свой свод законов, но с блокировкой изменений ото всех. Важно понимать, что здесь нужен консенсус на чтение и консенсус на запись. Обязательно оговорим, со скольких островов нужно получить информацию, чтобы заявить, что закон работает на 100%. Нужно знать, сколько гонцов необходимо послать на другие острова, чтобы убедиться, что закон записан и теперь мы ему будем следовать.
Чтение — это сбор логов изменения законов к себе. Запись — этот лог изменения законов мы куда-то везём. Именно это — два консенсуса: на чтение и запись.
Если поменять острова на серверы, ничего не пропадёт: как рассуждали, так и останется.
Острова и мосты — это уже традиция. Создатель этого алгоритма Лесли Лэмпорт использует аналогию из алгоритма Паксос. Его конкретные реализации — это ZAB (Apache ZooKeeper) и Raft. Если брать алгоритм, который соответствует Паксосу, то на выходе получится CP.
Спецификации говорят, что Raft простой, но на деле понять его нелегко. На сайте raft.github.io размещена классная настраиваемая браузерная анимация, с которой мы рекомендуем поиграть.
Практика
А теперь представим, что нас есть стажёр, который в первый раз пришёл на работу и впервые видит этот код. Напомним, модуль взят из курса «Мидл Python-разработчик»: на вход получает ссылку, на выходе отдаёт новый хэш, при переходе по хэшу он его разворачивает и даёт редирект. Кажется, просто — погнали!
Заходим в readme. Здесь написано, что это код для вебинара, и описано, как запустить Docker Compose, который облегчает задачу взаимодействия сервисов между собой.
Автор сервиса оставил нам документацию — большое спасибо. Это заслуга FastAPI: документация генерируется автоматически.
У нас есть сервис и файлы конфигурации. Разберём их.
В Dockerfile всё по классике: Python через pip и pipenv, последний уже ставит всё остальное, запускается API на порт 8080.
В файле docker-compose всё просто: Redis и наше приложение на Python. Го его реализовывать.
Мы попали в начало приложения. Здесь какие-то хуки на начало и конец, startup, shutdown. И вот самое интересное: в этом router подвязаны основные ручки.
Чуть не пропустили FastAPI. Кстати, что такое FastAPI?
FastAPI — это классный современный асинхронный фреймворк на Python. Внутри него — pydantic, генерация спецификаций OpenAPI (даже третья версия, а не Swagger!). Просто берёшь и пользуешься: описываешь входные и выходные данные pydantic для валидации, даёшь готовую документацию для других юнитов.
И при этом он асинхронный, что даёт дешёвую работу по сети. Сейчас речь идёт про код, который ждёт других операций: обращения к большому файлу на диске или сетевому ресурсу.
Итак, если у нас не монолитная структура, а микросервисная, то по процессорам и серверам будет значительно дешевле брать асинхронный фреймворк, потому что у него внутри есть event loop, который будет экономить время. Не будем сильно ударяться в event loop, потому что его мы подробно разбираем на курсе — там это основа основ. Мы затрагиваем и корутины, которые идут мостиком к Golang и каналам.
Вернёмся к коду. Нам нужны функции «создать», «проверить» и «редиректнуть». Вот эти три функции.
Разберём, на мой взгляд, самую простую — создание URL. Проговорим логику:
Присутствует околослужебная информация, что мы заинициализировались. А ещё мы отслеживаем коллизии. Вопрос с коллизиями мы решаем добавлением букв. Мы не городим список и не хэшим на ключ, а просто меняем ключ.
Во второй части поста расскажем, зачем нужен Dynamo, и что делать, когда Redis несколько.