Ключевые слова:memcached, cache, optimization, web, (найти похожие документы)
From: Станислав
Date: Mon, 26 Sep 2010 17:02:14 +0000 (UTC)
Subject: Борьба с Dog-pile эффектом, используя установку локов в memcached
Оригинал: http://korchasa.blogspot.com/2008/04/dog-pile.html
Dog-pile эффект -- ситуация когда кэш протухает, а большое количество
запросов генерирует высокую нагрузку на источник данных, из которых
строиться кэш.
Представьте, что вы кэшируете результат какого то тяжёлого запроса,
например, список популярных статей. В какой-то момент времени кэш
протухает, и его кто-то должен построить заново. В общем то пока все
хорошо. Кроме случаев когда построение кэша тяжёлая операция, а
запросов на него много. Например, запрос для генерации кэша занимает
1 секунду, а пользователи ломятся по 10 штуков в секунду.
Соответственно, 9 пользователей (кроме первого) будут только зря
нагружать базу. А при большом количестве запросов могут и полностью
ее положить.
И пусть весь мир подождёт
Первое, что нужно решить -- может ли пользователь ждать генерации
кэша. Пример с популярными статьями это цветочки, ибо есть еще сложно
рассчитываемые рейтинги, которые могут считаться оооочень долго.
Предположим, что у нас простой случай и пользователь не сломается,
если подождёт секунду-другую.
Честные блокировки на генерацию
Решение в лоб. Первый пришедший лочит кэш на генерацию, и
отправляется генерировать кэш. Остальные, увидев лок, понимают, что
не успели, чешут репы и думают, что делать.
Как именно делать лок зависит только от вашей фантазии и имеющихся
средств. Это может быть, что угодно:
Лок файловой системы есть косяки, но иногда работает :). Подробности в описании функции flock().
Мьютекс в хранилище
Если мы используем memcache и генерируемый кэш имеет ключ
"popular_articles", тогда наличие данных с ключем
"popular_articles_lock", говорит о том, что наш кэш уже кто-то
генерирует. Тоже самое справедливо и для других хранилищ.
IPC семафоры
Настоящие семафоры ;) Ни разу не использовал -- руки не доходят.
Плюсы локов в том, что клиент сам решает, что делать в ситуации,
когда ему нужны данные, а их кто-то генерирует. Например, если есть
старые данные, то мы можем их отдать, а если данных нет, то либо
подождать, либо честно вывести пользователю, что данных нет. На самом
деле висящие в течении секунды пользователи совсем не есть гуд, но
иногда можно допустить и такое.
Так же не стоит забывать, что при генерации может возникнуть ошибка,
и в этом случае лок может остаться висеть(в случае memcache можно
ставить ttl на лок).
Организация "окна" для генерации
Способ был подсмотрен в исходниках какого-то фреймворка :)
Суть его в том, что ttl поддерживается не самим хранилищем, а
клиентом, в виде отдельного ключа, и при истечение данные не
удаляются. Т.е. приходит первый запрос, видит что ttl истек, и
начинает генерировать новое значение. Чтобы остальные запросы
подумали, что все нормально, он продлевает ttl существующего кэша на
какую-то заранее определённую величину. Если писатель умирает, то кэш
быстро снова протухнет и кто-нибудь подхватит знамя генерации с трупа
павшего товарища. Если же все нормально, то будет записан новое
значение кэша и установлен новый ttl.
Основным минусом такого подхода является то, что ttl нельзя хранить
средствами самого кэш-storage, т.к. во всех самых известных
хранилищах невозможно получить значение ttl и сами данные с истекшим
ttl.
Я хочу вас всех, я хочу вас сразу!
Случай у нас тяжёлый, и пользователю будет скучно коротать 20 секунд
рисуя матом надписи на пыльном столе. Я знаю про кого будут эти
надписи.
Главное откровение: чтобы избавиться от последствий многопоточности
надо свести ее к одному потоку. Просто и со вкусом. Убираем всю
генерацию кэша в оффлайн. ttl-ем в данном случае будет время
перезапуска генерирующих кэш скриптов. Помимо быстрого ответа
пользователю тут есть еще один плюс -- наборы кэшей часто
генерировать быстрее, чем каждый из них по отдельности, ибо можно
хранить какие-то промежуточные данные.