Какие недостатки у хранения данных в памяти
Что проверяют
Видите ли вы цену быстрого решения: память быстрая, но конечная и исчезает при рестарте. Дальше обычно просят рассказать, как сохранить данные после падения и как держать кэш согласованным между несколькими инстансами.
Как отвечать
Сначала уточните, о чём речь: о структуре в куче JVM (ConcurrentHashMap, Caffeine) или об отдельном in-memory хранилище вроде Redis и Hazelcast. Недостатки у них частично общие:
- летучесть: рестарт, деплой или
OutOfMemoryErrorстирают всё. Redis смягчает это снапшотами RDB и журналом AOF, но сappendfsync everysecпри сбое можно потерять около секунды записей; - ограниченный объём: без политики вытеснения карта растёт до OOM. В Caffeine задают
maximumSizeиexpireAfterWrite, в Redismaxmemoryиmaxmemory-policy; - давление на GC: миллионы долгоживущих объектов в куче удлиняют паузы сборки. Выход: off-heap хранение или вынос данных в отдельный процесс;
- горизонтальное масштабирование: у каждого инстанса своя копия, запись на одном узле не видна на другом. Приходится вводить инвалидацию через pub/sub или переходить на распределённый кэш;
- холодный старт: после рестарта кэш пуст, и вся нагрузка разом идёт в базу. Нужен прогрев или ограничение запросов к источнику;
- конкурентный доступ:
HashMapиз нескольких потоков ломается, а составные операции надConcurrentHashMapбезcomputeилиputIfAbsentне атомарны.
Хороший ответ заканчивается правилом: в памяти держат то, что можно восстановить из источника истины, а источник истины живёт на диске.
Где спотыкаются
Делают static Map кэшем без ограничения размера. Через неделю работы это классическая утечка памяти.
Забывают, что за балансировщиком несколько инстансов. Сессия или счётчик в памяти одного узла тогда работают только при sticky sessions.
Считают Redis надёжным хранилищем «из коробки». Без настроенной персистентности и реплики это тот же кэш, просто в отдельном процессе.