Storage Layer — Слой хранения данных
Версия: 1.0
Дата: 19.04.2026
Статус: Черновик
Обзор
Storage layer обеспечивает надёжное хранение мастер-данных и быстрый доступ к часто запрашиваемой информации. Основан на гибридной архитектуре PostgreSQL + Redis согласно принципу разделения из раздела "### 3. Storage (Слой хранения)" документа Архитектура платформы.
Схема хранилища: См. диаграмму vitrip_storage_architecture.jpg — детальная схема PostgreSQL и Redis с указанием типов данных и связей.

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

Принцип разделения данных
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 limits | 1 час | По использованию | 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
- Read replicas — для аналитики и отчётов
- Connection pooling — PgBouncer для управления соединениями
- Партиционирование — по времени и регионам
- Sharding — по hotel_id при росте >10TB
Redis Scaling
- Redis Cluster — автоматический sharding по ключам
- Memory optimization — настройка eviction policies
- Реплики — для read-only операций
- Compress values — для экономии памяти
Связанная документация
- Общая архитектура: раздел "### 3. Storage (Слой хранения)" в Архитектура платформы
- Потоки данных: диаграмма
vitrip_data_flows.jpgпоказывает взаимодействие с кешем - Планируемые спецификации: детальная схема БД будет в
reference/reference/database-schema.md