Как организовать хранение и быстрый возврат данных при большом объеме и удаленной базе данных
Что проверяют
Умеете ли вы сначала выяснить требования, а потом подобрать средства, и понимаете ли, что у удалённой базы главный враг это число сетевых обращений и объём передаваемых данных. Следом обычно спрашивают, как держать кэш в актуальном состоянии.
Как отвечать
Начните с уточнений: объём и рост данных, соотношение чтения и записи, требования к задержке (p95, p99), допустимая свежесть данных, насколько база далеко по сети. Каждый запрос в соседний регион стоит миллисекунд, и сотня мелких запросов съедает любой SLA.
Дальше слои:
- меньше обращений: убрать N+1 в JPA через
JOIN FETCH,@EntityGraphили@BatchSize, писать пачками сhibernate.jdbc.batch_size, читать сразу по списку ключей; - меньше данных: проекции в DTO вместо целых сущностей, пагинация по ключу (keyset) вместо
OFFSET, который на глубоких страницах читает и отбрасывает всё предыдущее. Большие выборки читают потоком сfetchSize, а неfindAll()в память; - соединения: пул HikariCP (по умолчанию в Spring Boot) с разумным размером и тайм-аутами;
- кэш: локальный Caffeine для горячих справочников, Redis для общего кэша между инстансами. Схема cache-aside, TTL и инвалидация при записи, в Spring это
@Cacheableи@CacheEvict; - чтение отдельно от записи: реплики с учётом задержки репликации, заранее посчитанные агрегаты, материализованные представления;
- масштаб: партиционирование по дате или ключу, шардирование, когда упёрлись в один узел.
Упомяните параллелизм: независимые запросы можно выполнять одновременно через CompletableFuture или виртуальные потоки Java 21. И мониторинг: медленные запросы, EXPLAIN ANALYZE, доля попаданий в кэш, метрики пула.
Где спотыкаются
Сразу предлагают Cassandra или «переехать на NoSQL», не спросив про паттерн доступа. Смена базы решает редкую задачу, а N+1 и отсутствие индекса встречаются постоянно.
Кэшируют без плана инвалидации. Данные расходятся с базой, а когда истекает популярный ключ, сотни запросов разом идут в базу.
Используют OFFSET на миллионах строк и грузят коллекции целиком, а потом добавляют память и потоки вместо того, чтобы уменьшить выборку.