Memcached часто воспринимают как «простую» RAM-помойку: положил значение по ключу — забрал обратно. В production всё быстро становится сложнее: память вроде есть, но вдруг начинается eviction; часть ключей «горячие» и давят весь пул; при падении кэша база получает лавину запросов (cache stampede); а ограничение на подключения внезапно превращается в источник таймаутов.
Ниже — практический разбор: как Memcached управляет памятью через slab allocator, почему размер item критичен, как диагностировать и уменьшать eviction, и какие метрики (stats) реально помогают при tuning в production.
Модель памяти Memcached: почему slab allocator важнее, чем «свободная RAM»
Memcached не выделяет память под каждый объект отдельно «как получится». Он делит выделенную память (-m) на классы (slab classes): каждый класс обслуживает объекты определённого диапазона размеров. Внутри класса память режется на страницы (slabs), а страницы — на одинаковые чанки (chunks). Каждый item занимает ровно один chunk своего класса.
Следствие простое, но неприятное: если вы кладёте объекты разных размеров, память распределяется по классам и не «перетекает» свободно между ними. Легко получить ситуацию, когда в одном классе есть запас, а в другом — дефицит и постоянные вытеснения, хотя суммарно «ещё гигабайты».
В production проблема часто не в общем объёме кэша, а в перекосе распределения по slab-классам из-за реальных размеров item.
Что такое item size на практике
Item в Memcached — это не только «полезная нагрузка» (value). Туда входят накладные расходы: ключ, флаги, TTL, CAS и метаданные. Поэтому значение на 800 байт может попасть в класс чанков на 1 КБ или 1.25 КБ — в зависимости от того, как настроены классы. В итоге 10–30% памяти может уходить во внутреннюю фрагментацию, если распределение размеров неудачное.
На это напрямую влияют сериализация (JSON/MsgPack/Proto), длина ключей (да, ключ тоже «ест» память) и пограничные размеры значений (например, «чуть-чуть больше» популярного чанка).
Eviction: почему Memcached выкидывает данные и как отличить «нормально» от «плохо»
Eviction — это вытеснение: Memcached удаляет старые элементы, чтобы освободить место под новые. В идеальной картине eviction бывает «контролируемым»: кэш удерживает рабочий набор, а редко используемое вытесняется.
Проблема начинается, когда eviction становится постоянным фоном, а hit rate падает: вы фактически крутите кэш как конвейер, нагружая сеть, CPU и бекенд, но не получая выигрыша.
Типовые причины «плохого» eviction
Перекос по slab-классам. Один класс переполнен и постоянно вытесняет, другой простаивает.
Неподходящий TTL. Слишком длинный TTL при маленькой памяти удерживает мусор; слишком короткий TTL провоцирует stampede.
Слишком большие item. Крупные объекты съедают страницы и быстрее выдавливают рабочий набор.
Hot keys. Небольшое число ключей получает львиную долю запросов и мешает остальным, особенно если значения крупные или часто перезаписываются.
Какие stats смотреть в первую очередь
Начинайте с базового: понять, есть ли давление на память и как оно проявляется.
echo "stats" | nc 127.0.0.1 11211
Поля, на которые обычно смотрят:
get_hitsиget_misses— основа для hit rate.evictions— факт вытеснений (важна динамика).bytesиlimit_maxbytes— занято и лимит.curr_items— сколько элементов сейчас хранится.reclaimed— сколько освобождено из-за истечения TTL (это «здоровое» освобождение, не eviction).
Дальше обязательно переходите к статистике по slab-классам — именно там видны перекосы.
echo "stats slabs" | nc 127.0.0.1 11211
echo "stats items" | nc 127.0.0.1 11211
Ищите классы, где растут eviction или где used_chunks близок к total_chunks, а в других классах есть заметный запас.

Slab allocator: как управлять распределением памяти, не ломая production
Главная практическая задача — сделать так, чтобы ваш реальный профиль item size «красиво ложился» на slab-классы, а рабочий набор помещался без постоянного eviction.
Проверьте лимиты размера объекта и стратегию хранения больших значений
У Memcached есть ограничение на максимальный размер элемента (-I, часто по умолчанию 1 МБ). Увеличить его можно, но это не всегда хорошая идея: крупные элементы резко ускоряют вытеснения и ухудшают эффективность slab-раскладки. Часто правильнее вынести большие значения в другое хранилище, а в Memcached держать «указатель» или компактный агрегат.
Отдельно проверьте, что вы не кэшируете данные, которые плохо переиспользуются: персональные ответы с низкой повторяемостью, одноразовые результаты и всё, что почти всегда уникально.
Настройка роста slab-классов и базового размера чанка
Два параметра, которые сильнее всего влияют на внутреннюю фрагментацию:
-n— минимальный размер пространства под key+value+metadata (минимальный chunk). Если у вас много очень маленьких значений и коротких ключей, слишком большой-nбудет тратить память впустую.-f— фактор роста slab-классов (например, 1.25). Чем меньше шаг, тем точнее классы под реальные размеры item и меньше потерь на округление, но тем больше классов и накладных расходов на управление.
Рабочий подход: сначала измерить реальный профиль размеров (на уровне приложения, логированием или сэмплингом), затем подбирать -n и -f так, чтобы основные размеры попадали «плотно».
Автоматическое перемещение страниц между классами (slab reassign)
В некоторых режимах Memcached умеет перераспределять страницы между slab-классами. Это помогает при изменяющемся профиле нагрузки (например, ночью кэшируются отчёты крупнее, днём — мелкие карточки). Но это не «волшебная палочка»: перераспределение не отменяет проблем с большими item, неконтролируемыми TTL и штормами перезаписи.
Если вы включаете/используете slab reassign, контролируйте задержки и влияние на hit rate в моменты миграции страниц.
Hot keys: когда кэш становится точкой перегрева
Hot keys — ключи, к которым обращаются непропорционально часто. В сочетании с высоким QPS, большим числом клиентов и частыми перезаписями это приводит к росту латентности и к тому, что «холодные» ключи начинают страдать от вытеснений.
Что делать с hot keys на уровне приложения
Шардирование горячего ключа. Вместо одного ключа используйте N ключей с суффиксом (например, по хешу идентификатора), а затем агрегируйте. Подходит не всем типам данных, но часто спасает CPU/сетевой стек.
Разделение кэшей по доменам данных. Часто лучше иметь несколько инстансов/пулов Memcached: отдельно для сессий, отдельно для каталога, отдельно для тяжёлых выборок. Это снижает взаимное вытеснение и упрощает tuning.
Уменьшение размера значения. Нормализовать структуру, хранить только нужные поля, пересмотреть сериализацию.
Если вы разносите кэши по ролям, обычно удобнее держать такие пулы на отдельных виртуалках, чтобы изоляция была не только логической. Для этого чаще берут VDS под кэш-узлы, чтобы гарантировать ресурсы CPU/RAM и предсказуемую сеть.
Cache stampede: как не «убить» базу при истечении TTL
Cache stampede возникает, когда множество запросов одновременно обнаруживают, что ключ отсутствует или истёк, и идут строить значение в бекенде. В результате кэш не спасает, а наоборот становится триггером лавины.
Рабочие антиштормовые приёмы
TTL jitter. Добавляйте случайное смещение TTL (например, ±10%). Это размажет массовые истечения.
Dogpile protection (локи на пересборку). При промахе один воркер строит значение, остальные ждут или получают «старое» значение (если оно есть в другом месте).
Stale-while-revalidate на уровне приложения. Храните «мягкий TTL» и «жёсткий TTL»: после мягкого отдаёте старое и обновляете асинхронно, после жёсткого — обязаны пересобрать.
Негативное кэширование. Коротко кэшируйте «нет данных», чтобы не долбить бекенд по несуществующим сущностям.
Если у вас похожие проблемы возникают и с HTTP-кэшем на фронтенде, полезно сверить подходы с материалом про управление TTL в Nginx: Nginx secure link и TTL для контролируемого кеширования.

Connection limit и сетевые узкие места: когда Memcached быстрый, а приложение — нет
Частая production-проблема — не память, а подключения: слишком много клиентов, короткие соединения, отсутствие пула, агрессивные таймауты или упор в лимиты ОС по файлам.
Симптомы
рост
curr_connectionsиtotal_connectionsпри тех же QPS;таймауты на клиенте и рост ретраев;
ошибки отказа в новых соединениях, если упёрлись в лимит Memcached или
ulimit -n.
Что проверить по stats
echo "stats" | nc 127.0.0.1 11211
Смотрите в первую очередь:
curr_connections,total_connections— текущие и суммарные подключения;rejected_connections— были ли отказы;listen_disabled_num— сколько раз Memcached отключал приём новых соединений из-за перегруза.
Дальше — на стороне ОС: лимиты открытых файлов, очередь accept, базовые TCP-настройки, а главное — модель в приложении (пул соединений, keepalive, таймауты на операции и ретраи с backoff).
Практический чеклист tuning для Memcached production
Порядок действий, который обычно даёт предсказуемый результат и не превращает tuning в гадание:
Соберите базовые метрики. Hit rate,
evictions,reclaimed,bytes/limit_maxbytes, подключения, ошибки/таймауты на клиенте.Проверьте распределение по slab-классам. Снимите
stats slabsиstats items, найдите «перегретые» классы и классы с явным запасом.Убедитесь, что item size соответствует ожиданиям. Измерьте реальные размеры сериализованных значений и длины ключей на стороне приложения.
Стабилизируйте TTL. Добавьте jitter, внедрите защиту от stampede для самых горячих ключей.
Разделите домены кэша при необходимости. Если разные типы данных конфликтуют в slab-распределении — разнос по инстансам часто эффективнее бесконечных попыток подобрать
-f.Проверьте подключения и лимиты ОС. Пул соединений и адекватные лимиты часто дают больше, чем увеличение памяти.
Только затем меняйте параметры Memcached. Сначала корректируйте модель данных, потом —
-m,-n,-f,-I.
Мини-ранбук диагностики: «eviction растёт, а памяти вроде достаточно»
Если вы видите рост evictions, но bytes заметно ниже limit_maxbytes, это почти всегда история про slab allocator.
Снимите
stats slabsи посмотрите, в каких классах есть давление (заняты чанки, растут eviction).Снимите
stats itemsи оцените «возраст» и интенсивность перезаписи по классам (где элементы живут слишком долго, а где постоянная мясорубка).Сопоставьте с профилем размеров item (из приложения). Обычно находится «ступенька»: много объектов чуть больше популярного размера chunk.
Выберите самое дешёвое действие: уменьшить/изменить формат объектов, разделить кэш по пулам или перенастроить
-f/-nи пересчитать классы.
Итоги
Memcached в production — это в первую очередь управление профилем данных и поведением приложения, а не «добавим памяти и станет лучше». Slab allocator заставляет мыслить распределением по классам, item size напрямую влияет на эффективность использования RAM, а eviction нужно интерпретировать вместе со stats slabs и stats items.
Стабильная работа достигается комбинацией: правильные TTL (с jitter), защита от cache stampede, контроль hot keys, адекватные лимиты подключений и осознанный tuning параметров Memcached под реальный профиль нагрузки.
Если вы выбираете между Memcached и Redis под разные задачи кэширования (и хотите понять, где какие грабли в эксплуатации), пригодится сравнение: Memcached и Redis для PHP-кэша: выбор и типовые ошибки.


