Проекты
Система инвалидации кеша для PostgreSQL
Тип участника:
Физическое лицо
Полное наименование организации/физического лица/авторского или творческого коллектива:
Ткаченко Алексей Алексеевич
ФИО всех участников авторского/творческого коллектива:
Ткаченко Алексей Алексеевич
Идея и краткое описание ИТ-проекта:
Разработка автоматизированного сервиса для обеспечения консистентности данных между кешем на Redis и основной базой данных на PostgreSQL путем отслеживания изменений на уровне журнала предзаписи записи без вмешательства в бизнес-логику приложений.
В современных высоконагруженных информационных системах для ускорения доступа к данным широко используется кеширование. Однако при изменении данных в основной базе данных возникает проблема актуальности кеша, так как устаревшие данные продолжают отдаваться пользователям, это классическая проблема инвалидации, которая приводит к принятию неверных решений, финансовым потерям и снижению доверия к системе.
Есть 2 традиционных подхода к решению данной проблемы:
- производить удаление неактуальных записей из кеша в клиентском приложении
- удаление кеша по таймеру
При инвалидации на стороне клиентского приложения возникает совмещение бизнес-логики клиентского приложения и логики инвалидации, что может быть чревато ошибками. Кроме того если с БД и кешем работают несколько приложений, логика инвалидации будет дублироваться. Удаление кеша по таймеру так же имеет недостатки. Во-первых между обновлением данных и инвалидацией по таймеру кеш будет выдавать неактуальные данные. Во-вторых возможны резкие всплески нагрузки на БД, в случае если у многих записей истек таймер.
Предлагаемое решение - независимый сервис, который отслеживает изменения в БД через чтение журнала предзаписи и удаляет неактуальные ключи из кеша.
Предлагаемое решение - независимый сервис, который отслеживает изменения в БД через чтение журнала предзаписи и удаляет неактуальные ключи из кеша.
- централизованное управление инвалидацией кеша без модификации существующих приложений
- снижение нагрузки на БД за счет исключения периодический синхронизаций в сравнении с инвалидацией по таймеру
- повышение надежности и отказоустойчивости системы
- простота внедрения и настройки
Перечень решаемых задач:
- Автоматизация инвалидации кеша - устранение необходимости ручной синхронизации Redis с PostgreSQL в каждом приложении, работающем с базой данных
- Обеспечение консистентности данных - гарантия того, что пользователи всегда получают актуальную информацию, исключая ситуации выдачи устаревших данных из кеша
- Снижение нагрузки на базу данных - отказ от периодических синхронизаций в пользу точечной инвалидации только измененных ключей
- Упрощение архитектуры приложений - вынос логики инвалидации кеша из бизнес-кода в отдельный независимый сервис, что соответствует принципу разделения ответственности
- Централизованное управление - единая точка конфигурации для всех правил инвалидации вместо распределенных настроек в каждом микросервисе
- Снижение стоимости разработки и поддержки - исключение дублирования кода инвалидации кеша в десятках сервисов, исключение проблемы когда разработчик забыл прописать логику инвалидации
- Поддержка цифровой трансформации - создание инфраструктурного компонента, необходимого для построения современных высоконагруженных систем в рамках цифровизации региона
Описание функциональных возможностей и элементов проекта:
Функциональные возможности:
- Подключение к PostgreSQL в качестве логического репликационного клиента через протокол streaming replication
- Чтение и парсинг WAL-журнала в реальном времени с использованием механизма logical decoding
- Фильтрация изменений по заданному списку отслеживаемых таблиц и схем
- Извлечение первичных ключей и значений полей из событий UPDATE, DELETE
- Формирование ключей Redis по настраиваемым шаблонам
- Атомарное удаление устаревших ключей из Redis с использованием команды UNLINK
- Автоматическое создание replication slot и publication в PostgreSQL при первом запуске если не настроены
- Graceful shutdown с корректным завершением обработки текущих изменений
- Конфигурация через YAML файл
Дата внедрения (в случае, если предполагается запуск проекта в эксплуатацию):
-
Используемые платформы, средства разработки:
- Язык программирования - Golang 1.25
- БД - PostgreSQL 16
- Хранилище кеша - Redis 7
- Взаимодействие с PostgreSQL - pgx v5.9.1
- Взаимодействие с Redis - go-redis v9.18.0
- Логическая репликация и разбор потока WAL - pglogrepl v0.0.0-20260401131349-e37c41485510
- Тестирование - testify, testcontainers-go
- Контейнеризация - Docker, Docker compose
- Контроль версий - Git, GitHub
Стоимость разработки системы:
Система разработана для дипломного проекта, доработку оцениваю в 1 200 000р
Средний размер ежегодных затрат на эксплуатацию:
240 000р
Перспективы развития:
- Поддержка других популярных СУБД (MySQL, SQLite) и аналогов Redis (memcached, dragonfly)
- Опциональная интеграция с брокерами сообщений
- Поддержка расширенного мониторинга через Prometheus и Grafana
- Изменение параметров системы прямо во время работы, без перезапуска
- Web-интерфейс
- Поддержка множества экземпляров баз данных и хранилищ кеша (на данном этапе возможно только с использованием прокси)
- Коммерциализация в виде SaaS-решения для малого и среднего бизнеса
- Адаптация для использования в региональных государственных информационных системах (ЕГИССО, РМИС, системы межведомственного взаимодействия)
Достижение поставленных целей:
Поставленные в рамках выпускной квалификационной работы задачи были решены. Цель работы была достигнута. Проект имеет практическую значимость. Имеется подробное README, хорошее покрытие unit и интеграционными тестами, возможность запуска тестового окружения для ручного тестирования.
Актуальность, экономическая или социальная полезность:
Актуальность проекта обусловлена растущими требованиями к производительности и отказоустойчивости современных информационных систем. Кеширование в Redis стало отраслевым стандартом для снижения нагрузки на СУБД и ускорения отклика приложений. Однако поддержание консистентности данных между кешем и базой данных остается сложной инженерной задачей, особенно в условиях высокой частоты изменений. Одновременно наблюдается устойчивая тенденция перехода к микросервисной архитектуре, которая позволяет значительно упростить горизонтальное масштабирование и повысить гибкость разработки. В таких условиях традиционные подходы к инвалидации кеша, встроенные в бизнес-логику каждого сервиса, становятся неэффективными и трудно поддерживаемыми.
В связи с этим создание отдельного микросервиса, отвечающего за автоматическую инвалидацию устаревших данных и недопущение выдачи неактуальной информации пользователям, является логичным архитектурным решением, полностью соответствующим современным трендам в разработке распределенных систем. Такой подход обеспечивает централизованное управление, упрощает масштабирование и повышает общую надежность инфраструктуры.
Экономическая полезность проекта проявляется в нескольких аспектах. Во-первых, снижается нагрузка на СУБД за счет отказа от периодических полных синхронизаций и неоптимальных запросов. Во-вторых, существенно сокращается время разработки новых сервисов, поскольку их логика освобождается от инфраструктурных задач по работе с кешем. В-третьих, минимизируются риски финансовых потерь, связанных с принятием решений на основе неактуальных данных (например, отображение устаревших цен или остатков товаров), а также снижаются риски ошибок разработчиков при ручной инвалидации кеша. Для бизнеса Алтайского края внедрение подобного решения позволит создавать более надежные и производительные информационные системы с меньшими затратами и в более короткие сроки, что повысит конкурентоспособность региональных IT-компаний на федеральном рынке.
В связи с этим создание отдельного микросервиса, отвечающего за автоматическую инвалидацию устаревших данных и недопущение выдачи неактуальной информации пользователям, является логичным архитектурным решением, полностью соответствующим современным трендам в разработке распределенных систем. Такой подход обеспечивает централизованное управление, упрощает масштабирование и повышает общую надежность инфраструктуры.
Экономическая полезность проекта проявляется в нескольких аспектах. Во-первых, снижается нагрузка на СУБД за счет отказа от периодических полных синхронизаций и неоптимальных запросов. Во-вторых, существенно сокращается время разработки новых сервисов, поскольку их логика освобождается от инфраструктурных задач по работе с кешем. В-третьих, минимизируются риски финансовых потерь, связанных с принятием решений на основе неактуальных данных (например, отображение устаревших цен или остатков товаров), а также снижаются риски ошибок разработчиков при ручной инвалидации кеша. Для бизнеса Алтайского края внедрение подобного решения позволит создавать более надежные и производительные информационные системы с меньшими затратами и в более короткие сроки, что повысит конкурентоспособность региональных IT-компаний на федеральном рынке.
Масштабируемость, способность к взаимодействию с другими системами, мобильность:
Горизонтальное масштабирование возможно с использованием прокси. На данном этапе система способна работать только с 1 экземпляром Redis, однако работа с несколькими экземплярами возможна если установить соединение с прокси-серверами, которые будут перенаправлять запросы другим экземплярам. Это возможно с использованием таких решений как Redis Cluster. Работа с несколькими экземплярами PostgreSQL не предусмотрена, так как для работы сервис читает WAL только одной БД. Возможным решением на данном этапе станет запуск нескольких сервисов, подключенных к каждой БД. Так как сервис может быть запущен в docker контейнере для горизонтального масштабирования проблем нет.
Сервис может работать с Kubernetes, имеются heathz и ready эндпоинты. По умолчанию сервис пишет логи в stdout, так что сбор логов возможен через ELK и/или Loki.
Сервис тестировался на PostgreSQL v16 и Redis v7, однако логическая репликация PostreSQL доступна с 10 версии. Для удаления ключей из Redis используется команда UNLINK, так что возможна совместимость с версиями Redis 4+.
Сервис написан на Golang, компилятор которого поддерживает компиляцию под практически все системы x86, amd64, ARM (включая Raspberry Pi), ARM64, MIPS, PPC64, RISC-V, S390x.
Обоснованность применяемых проектных решений:
Выбор YAML в качестве формата конфигурации обусловлен его человекочитаемостью и статусом стандарта в cloud-native экосистеме. YAML нативно поддерживается Kubernetes, Docker Compose, системами CI/CD и инструментами автоматизации, что критически важно для интеграции сервиса в современную инфраструктуру. Формат поддерживает сложные структуры данных, включая вложенные объекты и массивы, что позволяет гибко описывать правила преобразования ключей Redis. Альтернативы имеют существенные недостатки: JSON не поддерживает комментарии, TOML менее распространен, а переменные окружения ограничивают выразительность и создают проблемы с безопасностью.
Отслеживание изменений через чтение WAL-журнала PostgreSQL обеспечивает полноту и надежность получения информации об изменениях. WAL содержит все изменения данных без исключений, а механизм логической репликации оптимизирован для минимальной нагрузки на базу. Репликационные слоты гарантируют сохранность WAL-записей до их подтверждения потребителем, исключая потерю изменений при временной недоступности сервиса. Альтернативный подход с триггерами создает дополнительную нагрузку на СУБД, усложняет схему базы и требует развертывания очереди сообщений. В реальных средах PostgreSQL обычно имеет реплики, поэтому подключение дополнительного логического клиента не увеличивает архитектурную сложность.
Вынос логики инвалидации в отдельный сервис реализует принцип разделения ответственности. Когда эта логика встроена в бизнес-код каждого приложения, возникает дублирование кода, сложность поддержки и высокий риск ошибок. Отдельный сервис централизует управление правилами инвалидации, освобождает приложения от инфраструктурной логики, упрощает тестирование и обеспечивает лучшую масштабируемость. При добавлении новых микросервисов не требуется реализовывать в них логику инвалидации, достаточно настроить правила в конфигурации сервиса.
Ограничение только удалением устаревших ключей без их обновления обеспечивает максимальную гибкость. В реальных приложениях кеш редко содержит простые дубликаты записей БД, чаще это сложные агрегированные структуры, результаты аналитических запросов или данные в различных форматах сериализации. Автоматическое обновление кеша потребовало бы описания сложной логики формирования для каждого типа данных и создало бы дополнительную нагрузку на PostgreSQL. Подход с удалением ключей (cache-aside) помечает кеш как неактуальный, а следующее приложение формирует его заново при необходимости. Это обеспечивает консистентность данных, минимизирует нагрузку на БД и сохраняет гибкость для разработчиков.
Выбор команды UNLINK вместо DEL обусловлен производительностью. Команда DEL выполняет синхронное удаление и может блокировать основной поток Redis при удалении больших ключей, увеличивая латентность для других клиентов. UNLINK выполняет удаление асинхронно в фоновом потоке, не влияя на обработку других команд. Это критично для сервиса, обрабатывающего тысячи изменений в секунду.
Удаление ключей по одному без батчинга минимизирует задержку между изменением данных и инвалидацией кеша. Батчинг накапливает ключи в течение временного окна перед удалением, что увеличивает время, в течение которого пользователи могут получать устаревшие данные. Для критически важных данных эта задержка неприемлема. Удаление по одному обеспечивает минимальную задержку, ключ удаляется сразу после обработки изменения из WAL.
Отслеживание изменений через чтение WAL-журнала PostgreSQL обеспечивает полноту и надежность получения информации об изменениях. WAL содержит все изменения данных без исключений, а механизм логической репликации оптимизирован для минимальной нагрузки на базу. Репликационные слоты гарантируют сохранность WAL-записей до их подтверждения потребителем, исключая потерю изменений при временной недоступности сервиса. Альтернативный подход с триггерами создает дополнительную нагрузку на СУБД, усложняет схему базы и требует развертывания очереди сообщений. В реальных средах PostgreSQL обычно имеет реплики, поэтому подключение дополнительного логического клиента не увеличивает архитектурную сложность.
Вынос логики инвалидации в отдельный сервис реализует принцип разделения ответственности. Когда эта логика встроена в бизнес-код каждого приложения, возникает дублирование кода, сложность поддержки и высокий риск ошибок. Отдельный сервис централизует управление правилами инвалидации, освобождает приложения от инфраструктурной логики, упрощает тестирование и обеспечивает лучшую масштабируемость. При добавлении новых микросервисов не требуется реализовывать в них логику инвалидации, достаточно настроить правила в конфигурации сервиса.
Ограничение только удалением устаревших ключей без их обновления обеспечивает максимальную гибкость. В реальных приложениях кеш редко содержит простые дубликаты записей БД, чаще это сложные агрегированные структуры, результаты аналитических запросов или данные в различных форматах сериализации. Автоматическое обновление кеша потребовало бы описания сложной логики формирования для каждого типа данных и создало бы дополнительную нагрузку на PostgreSQL. Подход с удалением ключей (cache-aside) помечает кеш как неактуальный, а следующее приложение формирует его заново при необходимости. Это обеспечивает консистентность данных, минимизирует нагрузку на БД и сохраняет гибкость для разработчиков.
Выбор команды UNLINK вместо DEL обусловлен производительностью. Команда DEL выполняет синхронное удаление и может блокировать основной поток Redis при удалении больших ключей, увеличивая латентность для других клиентов. UNLINK выполняет удаление асинхронно в фоновом потоке, не влияя на обработку других команд. Это критично для сервиса, обрабатывающего тысячи изменений в секунду.
Удаление ключей по одному без батчинга минимизирует задержку между изменением данных и инвалидацией кеша. Батчинг накапливает ключи в течение временного окна перед удалением, что увеличивает время, в течение которого пользователи могут получать устаревшие данные. Для критически важных данных эта задержка неприемлема. Удаление по одному обеспечивает минимальную задержку, ключ удаляется сразу после обработки изменения из WAL.
Оригинальность, новизна, отличие от аналогов либо отсутствие аналогов:
На рынке существуют решения для синхронизации кеша с БД:
- Debezium - сервис CDC, имеется избыточная сложность, требует обязательного развертывания Kafka, высокая стоимость инфраструктуры
- pg-repo/pg-cache - расширения для PostgreSQL, ограниченная гибкость, отсутствие поддержки сложных паттернов ключей
- Периодическая синхронизация через cron - высокая нагрузка на БД, задержка актуальности данных (минуты/часы)
- TTL инвалидация (ограниченное время жизни кеша) - компромисс между актуальностью и нагрузкой, невозможность мгновенной инвалидации
Основная особенность проекта заключается в его узконаправленности. Он решает одну строго определенную проблему - недопускает получения неактуальных данных из кеша. Данная особенность позволила сделать проект простым в развертывании и настройке.
Гарантирую достоверность предоставленной в заявке информации. Подтверждаю, что организация не находится в состоянии ликвидации, банкротства, реорганизации (Только для организаций):
Да
Презентация проекта pdf:
Загрузить