Есть проверенная временем архитектура, которую я видел много раз для реализации веб-сервисов и приложений:
1. PostgreSQL для хранения реляционных данных
2. Redis для координации очередей фоновых заданий (и нескольких классических атомарных операций)
И Redis действительно прекрасен, но что если я скажу вам, что наиболее распространненые варианты его использования прекрасно покрываются функциональными возможностями PostgreSQL?
Пример использования №1: Очередь заданий
Возможно, самое частое использование Redis, которое я наблюдал - это выполнение задач в фоновом режиме. Идея состоит в том, чтобы выполнить какую-нибудь задачу в фоне (возможно с некоторыми входными данными) и гарантировать, что только один из воркеров выполнит его. Redis прекрасно справляется с этим, поскольку предоставляет богатый набор атомарных операций для своих структур данных.
Но с момента появления PostgreSQL 9.5 появилась SKIP LOCKED
опция для SELECT ... FOR ...
оператора (вот документация - https://www.postgresql.org/docs/9.5/sql-select.html#SQL-FOR-UPDATE-SHARE). Когда указывается данныая опция, 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
, нет шансов, что вы получите записи, заблокированные в другой транзкции (другим воркером). Не нужно беспокоиться о том, что несколько воркеров, выполняющих эту команду, получат одну и ту же строку из-за row-level блокировки.
Но здесь есть оговорка. Если у вас одновременно имеются большое количество воркеров, которые пытаются получить задания из очереди и большое количество неразобранных записей в таблице jobs, ситуация, при которой таблица будет "разбираться" на несколько воркеров путем блокировки записей, может занять некоторое время. Но как показывает практика, у большинства проектов, над которыми мне пришлось поработать, меньше дюжины фоновых воркеров и затраты вряд ли будут значительными.
Пример использования №2: Блокировки на уровне приложения
Представим типичную ситуацию, что у нас есть процедура синхронизации со сторонней служной и требуется, чтобы данная процедура не могла выполняться одновременно в разных процессах и\или на разных серверах. Это еще один распространенный кейс использования Redis - распределенные блокировки.
PostgreSQL также может помочь с этой задачей с помощью Advisory Locks (https://www.postgresql.org/docs/9.2/explicit-locking.html#ADVISORY-LOCKS). Данных механизм блокировок используется PostgreSQL для внутренних целей, но при этом имеется возможность использования данного механизма в своих приложениях.
Пример использования №3: Pub\Sub
Cамый крутой пример использования я оставил напоследок: отправка сообщений со стороны сервера и возможность подписаться на эти сообщения. Например, скажем, вам нужно уведомить пользователя о том, что у него есть новое сообщение. Или, возможно, вы хотите передать данные клиенту после их обработки в фоне, когда они становятся доступными. Обычно, веб-сокеты являются трансопртным уровнем для этих событий, а Redis - движком Pub\Sub.
Однако, начиная с 9-ой версии PostgreSQL также предоставляет эту функциональность с помощью операторов LISTEN
и NOTIFY
. Любой клиент PostgreSQL может подписаться (LISTEN
) на конкретный канал сообщений, который представляет из себя просто строковое значение. Когда другой PostgreSQL клиент отправляет сообщение (NOTIFY
) по этому каналу, все остальные подписанные клиенты будут уведомлены. По желанию можно прикрепить небольшое сообщение.
Если вы используйте Rails и ActionCable, использование PostgreSQL даже поддерживается из коробки (прим. автора: данная поддержка есть практически во всех популярных фреймворках (Django Celery, PHP Symfony Messenger, etc)).
Полные возможности использования PostgreSQL
Redis принципиально занимает другую нишу, чем PostgreSQL и прекрасно подходит во многих кейсах разработки программного обеспечения, куда PostgreSQL даже не стремится. Например, кэширование данных с TTL, а также хранение и управление эфемерными данными.
Однако, PostgreSQL имеет гораздо больше возможностей, чем вы зачастую ожидаете изначально, когда берете его только в качестве базы данных SQL для вашей любимой ORM.
И есть большая вероятность, что вещи, для которых вы тянете в проект Redis, могут оказаться по силам и вашему PostgreSQL. И возможно в каких то ситуациях стоит отказаться от Redis и сэкономить на эксплуатационных расходах и сложности разработки.