Как стать автором
Обновить

Комментарии 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 в гугл еще не завезли?)
Я очень давно не писал на php :) Сейчас поправлю, спасибо.
Пусть меня заминусуют, но я в упор не вижу, когда $flags !== FALSE
При memcache->get() либо:
— возвращает прочитаное значение ключа и возвращает связанные с ними флаги.
— возвращает FALSE в случае ошибки или отсутствия ключа

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

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

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

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

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

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


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