[Перевод] А нужен ли Redis или хватит PostgreSQL
Есть проверенная архитектура, которую я видел много раз для поддержки ваших веб-сервисов и приложений:
- PostgreSQL для хранения данных
- Redis для координации очередей фоновых заданий (и некоторых ограниченных атомарных операций)
Redis — это фантастика, но что, если бы я сказал вам, что его наиболее распространенные варианты использования этого стека на самом деле могут быть достигнуты с использованием только PostgreSQL?
Сценарий 1: очередь заданий
Пожалуй, наиболее частое использование Redis, которое я видел, — это координация отправки заданий из вашего веб-сервиса в пул фоновых воркеров. Идея состоит в том, что вы хотите записать желание выполнить какое-то фоновое задание (возможно, с некоторыми входными данными) и гарантировать, что только один из многих ваших фоновых воркеров выполнит его. Redis помогает в этом, поскольку предоставляет богатый набор атомарных операций для своих структур данных.
Но с момента появления версии 9.5 в PostgreSQL появилась функция SKIP
LOCKED для оператора SELECT… FOR… (документация здесь). Когда указана эта опция, PostgreSQL просто игнорирует любые строки, требующие ожидания снятия блокировки.
Рассмотрим этот пример с точки зрения фонового воркера:
BEGIN;
WITH job AS (
SELECT
id
FROM
jobs
WHERE
status = 'pending'
LIMIT 1
FOR UPDATE SKIP LOCKED
)
UPDATE
jobs
SET
status = 'running'
WHERE
jobs.id = job.id
RETURNING
jobs.*;
COMMIT;
При указании FOR UPDATE SKIP LOCKED блокировка на уровне строки неявно устанавливается для любых строк, возвращаемых из SELECT. Кроме того, поскольку вы указали SKIP LOCKED, этот оператор не будет заблокирован для другой транзакции. Если есть еще одно задание, готовое к обработке, оно будет возвращено. Нет никакого беспокойства о том, что несколько рабочих процессов, выполняющих эту команду, получат одну и ту же строку из-за блокировки на уровне строки.
Самая большая оговорка для этого метода заключается в том, что если у вас есть большое количество воркеров, пытающихся выполнить эту очередь, и большое количество заданий их снабжает, они могут потратить некоторое время, переходя между заданиями и пытаясь получить блокировку. На практике у большинства приложений, над которыми я работал, меньше дюжины фоновых воркеров, и затраты вряд ли будут значительными.
Сценарий 2: блокировки приложений
Представим, что у вас есть процедура синхронизации со сторонней службой, и вам нужен только один ее экземпляр, работающий для любого данного пользователя во всех серверных процессах. Еще одно распространенное приложение Redis, которое я видел: распределенная блокировка (distributed locking).
PostgreSQL также может добиться этого с помощью рекомендательных блокировок (advisory locks). рекомендательные блокировки позволяют вам использовать тот же механизм блокировки, который PostgreSQL использует для внутренних целей, для ваших собственных целей, определяемых приложением.
Сценарий 3: Pub/Sub
Самый крутой пример я оставил напоследок: отправка событий вашим активным клиентам. Например, предположим, что вам нужно уведомить пользователя о том, что у него есть новое сообщение, доступное для чтения. Или, возможно, вы хотите передавать данные клиенту, когда они становятся доступными. Обычно веб-сокеты являются транспортным уровнем для этих событий, в то время как Redis служит механизмом Pub/Sub.
Однако, начиная с версии 9, PostgreSQL также предоставляет эту функциональность с помощью операторов LISTEN и NOTIFY. Любой клиент PostgreSQL может подписаться (LISTEN) на конкретный канал сообщений, который представляет собой просто произвольную строку. Когда любой другой клиент отправляет сообщение (NOTIFY) по этому каналу, все остальные подписанные клиенты будут уведомлены. По желанию можно прикрепить небольшое сообщение.
Если вы используете Rails и ActionCable, использование PostgreSQL даже поддерживается из коробки.
Использование всех преимуществ PostgreSQL
Redis принципиально занимает другую нишу, чем PostgreSQL, и выделяется в том, к чему PostgreSQL не стремится. Примеры включают кэширование данных с TTL, а также хранение и обработку эфемерных данных.
Однако PostgreSQL имеет гораздо больше возможностей, чем вы можете ожидать, когда вы подходите к нему с точки зрения просто другой базы данных SQL или какой-то загадочной сущности, которая живет за вашей ORM.
Есть большая вероятность, что вещи, для которых вы используете Redis, могут оказаться хорошими задачами и для PostgreSQL. Возможно, стоит отказаться от Redis и сэкономить на эксплуатационных расходах и сложности разработки, полагаясь на несколько сервисов данных.