Когда ООМ убил memcached

Ситуация: на сайте есть несколько блоков данных, которые подгружаются с других сайтов и затем некоторое время хранятся в memcached. Это позволяет реже обращаться к другим сайтам и тем самым быстро выдавать страницы. Но в понедельник всё пошло не так, как было задумано.

Есть такая техника хранения данных в кэше, которая называется «Ленивый кэш» (Lazy Cache).

Схема такая:

  • Когда нужна информация, проверить наличие её в кэше
  • Если в кэше этой информации нет, то запросить из источника данных, затем сохранить информацию в кэш
  • Далее вернуть данные из кэша
Схема Lazy Cache  [ websequencediagrams.com ]

Схема Lazy Cache [ websequencediagrams.com ]

Схема позволяет при наличии данных в кэше работать очень быстро. При этом в кэше хранятся только реально необходимые данные.

В чём минус этой схемы? Если данных в кэше нет, то их надо получить из внешнего источника. И если на сайте используется блок, содержимое которого зависит от этого кэша, страница не будет выдана пользователю, пока источник данных не ответит. Если источник данных медленный, то и сам сайт тоже в этот момент начинает работать медленно. Если в этот момент несколько пользователей одновременно запрашивают страницу, содержащую такой блок, то все они «зависают», при этом он занимает память. При определённом количестве посетителей память сервера закончится.

Вот это и случилось в понедельник. Количество обращений на сайт превысило ожидаемое и память закончилась. OOM Killer решил освободить память и убил один из процессов, которые ему не нравились. Ему не понравился процесс memcached, который был запущен уже 90 дней и почему-то потреблял 8G памяти.

В итоге в цепочке Lazy Cache звено «если в кэше нет данных — запросить источник» стало самым слабым. Для генерации ответа каждому посетителю отправлялся запрос на сайт источника данных и в результате этот сайт тоже не выдержал нагрузки и упал.

Я знаю как минимум четыре способа решения этой проблемы. Сейчас эта проблема решена и я считаю решение очень надёжным. А как бы решил ты? Давай сверим наши решения.

Технические сведения:

  • сайт-источник (донор информации) — блог на WordPress
  • мой сайт (акцептор информации) — портал Icons8 на фреймворке Yii 1.x.
  • у меня на сервере есть PHP 5.6, MySQL 5.5, memcached, MongoDB, Redis

Павел Волынцев

Уже более 15 лет занимаюсь разработкой веб-проектов. Fullstack Senior Developer. IT евангелист — доношу свет знаний об информационных технологиях. Профессиональные цели: Дать людям возможность дать людям больше.

Читайте также:

  • Любой, кто укажет решение лучше моего, может претендовать на бонус. Жду твоих вариантов.

  • Vlad

    Что приходит сразу в голову кэш в БД или в файл, не претендует на решение лучше вашего, но интересно как сделали вы.

    • Да, у меня решение отличается.

      Кэш в базе данных всё равно не избавляет от проблем если memcached вдруг умер. Не столько из-за именно этой порции данных (несколько записей из блога), а потому что много процессов PHP начинают массово ломиться в базу данных.
      Именно с точки зрения доступности в данном случае кэш в файлы — лучше, чем в базе данных.

      • Vlad

        Думаю можно еще сделать кэш страницы на уровне nginx, если он используется.
        Выборка с БД по ключу, быстрый процесс, или есть нюансы?
        При обновлении данных с источника, даем знать другим процессам что обновление уже запущено.

        • > Выборка с БД по ключу, быстрый процесс, или есть нюансы?
          Ну только если много процессов PHP и недостаточно коннектов на чтение MySQL.

        • > При обновлении данных с источника, даем знать другим процессам что обновление уже запущено.
          Блокировка. Неплохо. Уменьшит нагрузку на внешний сервис, блог не упадёт.

          Но у меня как-бы умер мемкэш, получается что факт блокировки мне пока некуда записать.

          • Vlad

            Можем мониторить мемкэш с помощью supervisord, в случае недоступности кэш кладем в файл.

          • Да, можно использовать двойное кэширование

          • У нас используется monit. В том случае, если процесс memcached вдруг исчез, можно попробовать поднять автоматически и пожаловаться, если не поднимается.

    • Спасибо, что принял участие. Я и не надеялся уже 🙂

      Кстати, как бы ты обосновал — где лучше кэшировать — в файл или базу?

      • Сам отвечу на свой вопрос: кэш по скорости должен на порядок превосходить скорость получения данных.

        На примере:
        Если данные могут быть получены из источника за 1 секунду (курс валют с другого сайта), а из файлового кэша — за 0.1 секунду — это подходящий кэш.
        Если данные из файла читаются за 0.1 сек, а из memcached — за 0.01 сек — это тоже увеличение скорости на порядок, такой вариант подойдёт.

        Вторым критерием выбора кэша — объём данных. В одну запись memcached войдёт 2 мегабайта данных, а в файл можно сохранить гигабайт. Поэтому данные SQL запроса можно сохранить в memcached, а видео нужно в файл.

        • Aleksander Ivanovich

          Приглашаем Senior Ruby on Rails Developers 2000$-4000$

          компания «ETS Company Ltd. «, приглашает к сотрудничеству целеустремлённых и преданных своему делу Senior Ruby on Rails Developers для работы удалённо.

          — Компенсации в размере 2000-4000$

          Основные требования:

          — Ruby on Rail разработчик — senior level

          — 5 + лет опыта

          — знание английского обязательно, русского

          Условия сотрудничества:

          — полная занятость

          — Рабочее время с 8 до 17 по Гринвичу

          — Мы заключаем с вами соглашении о неразглашении информации

          Собеседование включает тест на 30 мин-3 часа (30 мин для профессионалов)

          • Привет, Александр. Интересное ты выбрал место для вакансии. Ты лично меня приглашаешь? Напиши на почту, пожалуйста. http://copist.ru/about/

          • Aleksander Ivanovich

            я пригласил вас в скап

    • За две недели больше нет участников.

      Записи блога меняются редко. Я сделал файловый кэш «с прогревом», который обновляется по крону пару раз в сутки.

      Считаю, что любой вариант кроме «Я не знаю» — это уже решение. Так что ты победил. Напиши мне через контакты http://copist.ru/about/ — договоримся о призе.

      Ещё можно было поставить monit и сделать кэш «с резервом»: обычный режим через memcached, а если memcached не доступен — то в файлы. Ещё можно было увести memcached на второй сервер, где кроме этого сервиса больше ничего нет, но нет столько ресурсов свободных.

      • Vlad

        Спасибо,
        не ради приза ответил, интересно было твое решение.