Комментарии 33
Это жесть какая то… Зачем каждый раз дергать внешний сервис, когда можно на крон повесить агента который обновлял бы 1 запись в базе, а уже все запросы формирования цены или просто вывода валюты брались из базы, и рядышком писать последнее обновление валюты 1 минуту назад или 3?
Просто пример выбран неправильно.
Замените file_get_contents на что-то другое, синхронное, длительное и с ошибками.
Впрочем, и в общем случае лучше такие операции выносить в cron.
Синхронное длительное и с ошибками это следующая часть.
Здесь про легкое быстрое и простое, где не сразу наступаешь на грабли…
Разве что да, стоило сказать что показываются разные пары валют — но это усложняло наглядность расчетов.
Как ни странно, описанная ситуация (правда, не для курсов валют) реальна: я её встречал десятки раз в жизни. И все шаги эволюции кэшестроения тоже.
Да, можно повесить демона в простейшем случае. И в сложнейшем тоже — об этом в следующей части.

Кстати, представьте, что показывается не одна пара, а для каждого пользователя своя. Запросы стоят каждый, таскать все пары получится слишком накладно…
Кеширование индивидуальных данных будет малоэффективным, раз ими может пользоваться малое кол-во запросов.
Кроме того, сделать один SELECT на 100к строк, и сделать 100к SELECT-ов по одной строке тоже сильно отличаются по нагрузке.
Статический поллинг горячих (не всех, а именно часто используемых) данных (с последующей записью в кеш) все-таки самый эффективный способ, если критично важно уменьшить нагрузку на источник данных.

Стоит также упомянуть, что блокировки тоже реализуются множеством способом — чем дальше от программы (как с использованием внешнего мемкеша, например) — тем больше задержки и накладные расходы. В пределах сервера все же лучше использовать SysV или хотя бы fcntl. Не говоря уже о разного типа мютексах или, для php, соответствующих модулях, где используется разделяемая память.
— Мы не знаем заранее, какие используются, особенно, когда пользователи могут поменять поведение в любой момент.
Но по закону Парето 80% запросов можно упихать в 20% кеша… Реальное распределение различается от случая к случаю. В случае с валютами, пары USD/RUR и EUR/RUR (+ обратные) скорее всего будут покрывать 80% случаев :)
— Хорошо, когда можно сделать SELECT на 100к строк. А еще лучше просто держать локальную реплику. Вот только при интеграции со внешними системами зачастую надо использовать тот API, какой есть.
— Да, рефреш кеша отдельным демоном это хорошо, об этом я писал в статье, и обязательно разверну в следующей части.

Крайне важно помнить, что если дать человеку сразу сложное решение, он спросит «нафига так сложно?» и сделает как ему кажется — просто и неправильно. И так пока не подорвётся.

Эта статья — ответ на вопрос, всплывавший многократно, почему кеш — не панацея, и вообще делает не то, что вы хотите. Если он применим в конкретном случае — хорошо. Но это совсем не очевидно, и при всей «очевидности» зачастую совсем не так.

— Статья совсем не о блокировках, и потому мы можем обсудить это в комментариях при желании — или вообще, напишете свою статью! Или я могу пройтись по вариантам — но только когда дойдут руки/ноги. Сказать можно много, вот только времени мало.

Напомню, что в статье мягко подразумевалось исполнение на appengine, так что это НЕ один сервер, а потому все способы блокировок, заточенные на single-server не имеют смысла.
Тогда плюсую, хотя и не могу) Если бы вы написали это в начале статьи, вопросов бы к вам небыло. А так спасибо.
Не понимаю, если честно. В начале статьи вполне себе сказано, что у нас «есть доступ к базе» и показан API, как именно доступ есть.
Или что, если слова «база» то сразу SQL?
Это я отвечаю тому, кто про «написали в начале стастьи» высказался :)
А уж кто из вас — я не знаю. Мы с моими меня вполне себе решаем такие вопросы единолично ;)
НЛО прилетело и опубликовало эту надпись здесь
А почему нельзя создать «демона» который будет обновлять данные в кеше каждые 5, 10, 30 секунд (в зависимости от времени суток или величина нагрузки или по какой-то иной логике), а пользвательские запросы всегда будут брать данные из кеша?
Только в статье описано почему это вредно, когда демон на PHP! Как там говорят, «Демон на PHP, Карл!» =)
Конечно он у вас сворует процесс и т.д. А если на Go/C/Crystal и тд демон? Он ни процессов PHP, ни время процессора, ни оперативки, ни сил и времени не будет отнимать.
Человека выше заминусили совсем зазря, ИМХО.
Простите, мы точно говорим об одной и той же статье? Можно циатату?
В конце статьи разбор с простым примером, где часть процессов PHP «висят» и не выполняют полезную работу.
Прочитайте еще раз. Речь идёт не об отдельном обновляющем демоне, а о том, что вместо 100 запросов в секунду мы обрабатываем 1 запрос 2 секунды, и не можем обрабатывать остальные так же быстро.

Это относится к поведению обработчиков при внезапном «утяжелении» запросов к зависямостям. Неважно кто это — php/go/c. Да даже на асме, если мы внезапно начали промахиваться мимо кеша на границе линии (так как злой пользователь загрузил картинку не 800*600 а 799*599), эффективная производительность проседает неожиданно и на порядки.

Ну даже если у нас многопоточный обработчик на Си, у нас точно так же ограниченное число тредов, которые могут обрабатывать запросы, и если один из них становится занят надолго — эффективная пропускная способность падает. И нет, мы не можем просто запустить еще один тред вместо висящего (равно как вредно у PHP cтавить максимум 200 процессов, чтобы «всё равно обрабатывать пока медленные висят», так как дополнительный обработчик занимает ресурсы — он не только «спит, ожидая ответа», он еще занимать память, диск, сокеты, файловые дескрипторы и т.п., и сложно заранее сказать когда и какой из таких ресурсов исчерпается и с какой стороны прилетит по голове.
Перечитал =) Я больше говорил о Вашей архитектуре работы с кэшем из PHP. И комментатор выше я думаю так же подумал.
Почему Вы не стали в PHP-приложении ТОЛЬКО ЧИТАТЬ значения из кэша? Ну и добавили бы минимум логики в приложение, мол получили значение из кэша — радуемся, а не получили — не рисуем компонент, вставляем дефолтное значение(если имеет место быть) и тд. А вот отдельный скрипт по крону запускается(или демон на постоянку с засыпанием), идет в чужой сервис и пишет итоги в кэш. PHP-приложение знать не знает, откуда в кэше данные. Поделили ответственность, создали задел на будущее и тд. В такой архитектуре я не вижу причин появления проблем из поста. С моих глаз у Вас борьба с ветряными мельницами: затолкали конкурентную запись с чтением в логику приложения с кучей клиентов/тредов и давай воевать там =)
Можно, автору все можно =) Я походу не осилил замысел статьи и/или не попал в целевую аудиторию статьи.
У меня к статье есть одна претензия: почему 1000 запросов в БД почему-то начало стоить денег?
Потому, что все хотят кушать. Заменим запрос курса валют на запрос стоимости билета на самолет, например, и цены изменятся на порядок. Хотя это всё те же запросы в БД…
$args = ["currency1=".urlencode($currency1), "currency2=".urlencode($currency2)];
$rate = @file_get_contents($api_host."?".join("&", $args));

http_build_query в гугл еще не завезли?)
Пусть меня заминусуют, но я в упор не вижу, когда $flags !== FALSE
При memcache->get() либо:
— возвращает прочитаное значение ключа и возвращает связанные с ними флаги.
— возвращает FALSE в случае ошибки или отсутствия ключа

то есть если значения нет, то флаги не меняются; если значение есть — флаги это число, соответственно тип сменится с boolean на integer, следовательно, $flags станет точно не равно FALSE.

(сравнение !== учитывает типы, то есть 0 == FALSE, но при этом 0 !== FALSE)
Пример мне кажется немного надуманным, действительно правильнее использовать под такую задачу крон. Но с академической точки зрения очень хорошая иллюстрация грабель.
Более уместным был бы пример, как мне кажется, тяжелых запросов в базу данных, которые зависят от данных профиля пользователя (например выдача новостей по интересам)
Это второй виток петли — вам кажется, что это нагляднее только потому, что первый виток уже очевиден.
Могу только апеллировать к грустному опыту наблюдений как народ проходит по этим граблям раз за разом даже для таких простых случаев.

Второй и третий витки в работе. Чукча не писатель, чукча инженер, поэтому это требует много времени, терпение-с плиз.
Конечно всегда найдутся еще грабли)
Но я хочу сказать, что если кто-то решает задачу из статьи прямо как написано, то это архитектурно не правильно. Задача веб сервера в том, чтобы как можно быстрее отработать request-response, а не мудрить сложную логику по вытягиванию данных и разруливанию race condition. Зачем множить это на кучи потоков, когда достаточно выделить лишь один, который будет заниматься конкретной задачей по актуализации данных в кеше.
Важно понимать, что это всего лишь паттерн: есть запрос, для него есть внешние данные, которые должны быть актуальны, но допускают некоторое отставание по времени.

Мы легко можем заменить во всей статье веб-фронтенд бэкенд API запросом, которому для вычисления нужна некая оценка курса. Или проверка факта что мы вообще сейчас выполняем эти запросы. Или еще что — миллион случаев, когда это вполне разумно сделать именно кешированием здесь и сейчас.

И несмотря на то, что для простого веб-фронтенда это действительно оверкилл, очень часто это реалии — начиная от shared hosting'ов где нет крона вообще (впрочем, выше уже писали, что я просто не умею настраивать несуществующие вещи), и заканчивая просто синдромом молотка (это когда всё кажется гвоздями, когда в руках молоток) у начинающих.

Собственно, из-за того, что уже несколько раз встречал синдром молотка, я и попытался описать в области максимально близкой для тех, кому это важно понять, принять и начать жить дальше уже иначе.
Развивая тему про новостей по интересам.
Сейчас подумал, ведь чисто теоритически можно составить примерную карту заходов пользователя, и на основании этого прогноза подготавливать кэш на данный момент?
Для всех не залогиненных пользователей показывать 'стандартный кэш новостей на даный момент'.
Цитата из статьи:
Иметь постоянно работающий фоновый процесс для обновления кэша: процесс должен с заданной периодичностью проверять, не устаревает ли значение в кэше, и если срок жизни подходит к концу, а значение требовалось за время жизни кэша – обновляет его.


Да, на этом этапе это было перебором, поэтому вопросы организации хранения значений в кеше в принципе за скобками (даже замена LRU на статистические оценки могут дать определённые преимущества, вот только всегда можно создать такую нагрузку на систему, которая отравит и такой кеш, а потому это становится не важно для оценки наихудшего сценария).
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Информация

Дата основания
Местоположение
США
Сайт
developers.google.com
Численность
свыше 10 000 человек
Дата регистрации

Блог на Хабре