Перейти к основному содержимому

Storage Layer — Слой хранения данных

Версия: 1.0
Дата: 19.04.2026
Статус: Черновик

Обзор

Storage layer обеспечивает надёжное хранение мастер-данных и быстрый доступ к часто запрашиваемой информации. Основан на гибридной архитектуре PostgreSQL + Redis согласно принципу разделения из раздела "### 3. Storage (Слой хранения)" документа Архитектура платформы.

Схема хранилища: См. диаграмму vitrip_storage_architecture.jpg — детальная схема PostgreSQL и Redis с указанием типов данных и связей.

Архитектура хранилища vitrip.store

Архитектура кеширования: См. диаграмму vitrip_cache_strategy.jpg — стратегии TTL, инвалидации и синхронизации между PostgreSQL и Redis.

Стратегии кеширования vitrip.store

Принцип разделения данных

PostgreSQL — Persistent Data (мастер-данные)

Назначение: Данные требующие консистентности, долгосрочного хранения и сложных запросов.

Типы данных:

  • Отели — статические характеристики, адреса, удобства
  • Контент — описания, фотографии, переводы
  • Бронирования — записи о резервах, статусы, история
  • Агенты — профили, настройки, права доступа
  • Справочники — локации, валюты, типы номеров

Redis — Volatile Data (кеш и временные данные)

Назначение: Данные с коротким жизненным циклом, высокой частотой обновлений.

Типы данных:

  • Цены — актуальные тарифы (TTL 5-15 минут)
  • Доступность — свободные номера (TTL 10-30 минут)
  • Результаты поиска — кешированные выборки (TTL 15-30 минут)
  • Сессии — пользовательские состояния (TTL 24 часа)
  • Счетчики — метрики, лимиты API (TTL 1 час)

PostgreSQL Architecture

Схема базы данных

Основные схемы:

  • public — общие справочники и настройки
  • hotels — данные об отелях и их характеристиках
  • content — мультимедиа контент и переводы
  • bookings — бронирования и связанные транзакции
  • users — агенты, роли и права доступа

Таблицы отелей (hotels schema)

hotels.properties

-- Основная таблица отелей
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
external_id VARCHAR(100) NOT NULL -- ID поставщика
supplier_id INTEGER REFERENCES public.suppliers(id)
name VARCHAR(200) NOT NULL
address JSONB NOT NULL -- Структурированный адрес
coordinates POINT -- PostGIS для геопоиска
star_rating DECIMAL(2,1)
chain_id INTEGER REFERENCES hotels.chains(id)
created_at TIMESTAMPTZ DEFAULT now()
updated_at TIMESTAMPTZ DEFAULT now()

hotels.supplier_mapping

-- Матчинг отелей между поставщиками
hotel_id UUID REFERENCES hotels.properties(id)
supplier_id INTEGER REFERENCES public.suppliers(id)
external_id VARCHAR(100) NOT NULL
confidence DECIMAL(3,2) -- 0.00-1.00 уверенность матчинга
verified BOOLEAN DEFAULT false
created_at TIMESTAMPTZ DEFAULT now()

-- Составной PK для уникальности пары поставщик-отель
PRIMARY KEY (hotel_id, supplier_id)

hotels.rooms

-- Типы номеров в отелях
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
hotel_id UUID REFERENCES hotels.properties(id)
room_type VARCHAR(100) NOT NULL
max_occupancy INTEGER NOT NULL
size_sqm INTEGER
amenities INTEGER[] REFERENCES public.amenities(id)[]

Партиционирование

По времени создания (бронирования):

-- Партиционирование таблицы бронирований по месяцам
CREATE TABLE bookings.reservations_y2024m01 PARTITION OF bookings.reservations
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');

CREATE TABLE bookings.reservations_y2024m02 PARTITION OF bookings.reservations
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');

По региону (отели):

-- Партиционирование отелей по регионам для геопоиска
CREATE TABLE hotels.properties_europe PARTITION OF hotels.properties
FOR VALUES IN ('EUR');

CREATE TABLE hotels.properties_asia PARTITION OF hotels.properties
FOR VALUES IN ('ASIA');

Индексы для производительности

-- Геопоиск отелей
CREATE INDEX idx_hotels_coordinates_gist ON hotels.properties USING GIST (coordinates);

-- Поиск по поставщику + внешний ID
CREATE UNIQUE INDEX idx_supplier_mapping_external
ON hotels.supplier_mapping (supplier_id, external_id);

-- Полнотекстовый поиск по названию отеля
CREATE INDEX idx_hotels_name_fts ON hotels.properties
USING GIN (to_tsvector('russian', name));

-- Поиск бронирований по дате заезда
CREATE INDEX idx_bookings_checkin_date ON bookings.reservations
USING BTREE (checkin_date) WHERE status = 'confirmed';

Redis Architecture

Naming Convention ключей

{namespace}:{entity}:{id}:{field}
hotel:prices:uuid:2024-04-19 # Цены отеля на дату
search:results:hash123:page1 # Результаты поиска
session:user:agent456 # Сессия агента
api:limits:partner789:hourly # Лимиты API партнёра

TTL Стратегии

Тип данныхTTLОбновлениеИнвалидация
Цены отелей5-15 минПо изменению у поставщикаManual + автоматически
Доступность номеров10-30 минПо бронированию/отменеНа событие booking
Результаты поиска15-30 минПо изменению ценНа обновление hotel/price
Сессии пользователей24 часаПо активностиManual logout
API rate limits1 часПо использованиюSliding window

Структуры данных Redis

Цены отелей (Hash)

HSET hotel:prices:uuid123:2024-04-19
"standard_room" "150.00"
"deluxe_room" "220.00"
"suite" "380.00"
"currency" "USD"
"updated_at" "2024-04-19T10:30:00Z"

EXPIRE hotel:prices:uuid123:2024-04-19 900 # 15 минут TTL

Результаты поиска (JSON)

SET search:results:hash456 '{"total":157,"page":1,"hotels":[{"id":"uuid123","name":"Grand Hotel","price":150.00}]}'
EXPIRE search:results:hash456 1800 # 30 минут TTL

API лимиты (Sliding Window)

# Sliding window для rate limiting
ZADD api:requests:partner789 1713556200 "request_id_001"
ZADD api:requests:partner789 1713556210 "request_id_002"
EXPIRE api:requests:partner789 3600 # 1 час окно

Синхронизация и консистентность

Write-Through Pattern

-- При обновлении цены в PostgreSQL
UPDATE hotels.pricing SET price = 150.00 WHERE hotel_id = 'uuid123';

-- Синхронно обновляем Redis
HSET hotel:prices:uuid123:2024-04-19 "standard_room" "150.00"

Cache Invalidation Events

Триггеры PostgreSQL → Redis:

-- Функция инвалидации кеша при обновлении отеля
CREATE OR REPLACE FUNCTION invalidate_hotel_cache()
RETURNS TRIGGER AS $$
BEGIN
-- Отправка события в NATS для инвалидации Redis
PERFORM pg_notify('cache_invalidation',
json_build_object(
'type', 'hotel_updated',
'hotel_id', NEW.id,
'timestamp', now()
)::text
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER hotel_cache_invalidation
AFTER UPDATE ON hotels.properties
FOR EACH ROW EXECUTE FUNCTION invalidate_hotel_cache();

Backup и Recovery

PostgreSQL Backup

# Полный backup с Point-in-time recovery
pg_basebackup -D /backup/base -Ft -z -P -W -h localhost -U backup_user

# WAL архивирование для PITR
archive_command = 'cp %p /backup/wal/%f'

Redis Persistence

# RDB snapshots каждые 15 минут при изменениях
save 900 1

# AOF для точного восстановления
appendonly yes
appendfsync everysec

Мониторинг и алертинг

Метрики PostgreSQL

  • Размер БД и рост по времени
  • Slow queries (>100ms) и их частота
  • Connection pool utilization
  • Lock wait time и deadlocks
  • Replication lag для read-реплик

Метрики Redis

  • Memory usage и eviction events
  • Cache hit ratio по типам данных
  • Key expiration rate
  • Network I/O и latency
  • Pub/Sub message backlog

Алерты

  • PostgreSQL connections > 80% — масштабирование пула
  • Redis memory > 90% — увеличение памяти/TTL
  • Cache hit rate < 85% — пересмотр стратегии кеширования
  • Replication lag > 5 секунд — проблемы с репликой

Масштабирование

PostgreSQL Scaling

  1. Read replicas — для аналитики и отчётов
  2. Connection pooling — PgBouncer для управления соединениями
  3. Партиционирование — по времени и регионам
  4. Sharding — по hotel_id при росте >10TB

Redis Scaling

  1. Redis Cluster — автоматический sharding по ключам
  2. Memory optimization — настройка eviction policies
  3. Реплики — для read-only операций
  4. Compress values — для экономии памяти

Связанная документация

  • Общая архитектура: раздел "### 3. Storage (Слой хранения)" в Архитектура платформы
  • Потоки данных: диаграмма vitrip_data_flows.jpg показывает взаимодействие с кешем
  • Планируемые спецификации: детальная схема БД будет в reference/reference/database-schema.md