Pull to refresh

Comments 87

> так как не все репозитории настолько богаты, как Debian-овский.

мне кажется вы заблуждаетесь…
> мне кажется вы заблуждаетесь…
Таки все?
да.
ибо дебиан _НЕ_ уступает набору софта в стандартной поставке только centOS
в остальном там общее кол-во пакетов не большое.
Я не спорю. Данный топик вообще не посвящён Операционным Системам. В некоторых системах нет скомпилированного memcached модуля, я нашёл предлог, чтобы собрать его из исходников. Что я написал не правильно?
Debian содержит меньшее количество пакетов потому, что Debian борется за «чистоту» рядов. Если начать учитывать всякую проприетарщину в non-free, то будет сравнимо, если не больше. Впрочем если посчитать еще и поддерживаемые архитектуры, то Debian явно в лидерах
Самое богацтво, по-моему, в Arch AUR — вот там есть почти всё, что только может быть душе угодно. Там даже виндовые программы в вайновской обвязке (например foobar2000) и досовские «со встроенным», так сказать, dosbox-ом (например я там неимоверно продвинутую версию Dune 2 нашёл, которую больше нигде не видел). Хорошо это или плохо — однозначно не скажешь — не совсем труъ конечно, зато страшно удобно и очень приятно иногда.
А можно код отформатировать, чтобы с отступами был? А еще избавиться, наконец, от "@" и «or die()».
Вы предлагаете в таком мануале «для новичков» бросать исключения?
1. нормально подсветить код
2. не учить новичков плохой практике.
А чем Вам не понравилась данная практика?
я говорю не про статью в целом, а про «сокрытие ошибок».
В чисто академических целях можно вообще не отлавливать исключительные ситуации. В мануале для новичков лучше просто поубирать "@" и «or die()». Так или иначе, ни в коем случае нельзя учить этим пользоваться, надо сразу приучать к дисциплине и хорошему коду.
Да впрочем вопрос из ничего. Кто как хочет, так и делает. Этому не научишь, не так ли?
Почему же? Нужно просто показывать положительный пример.
UFO just landed and posted this here
Сомнительная затея, помещать картинку на диск во временный файл, а потом читать оттуда для кеширования в памяти.
Лучше использовать ob_start() и ob_get_flush() для одновременной отдачи картинки клиенту и получения её содержимого.
Надо же было нагрузить ещё и диск:)
Я вот тоже задумался, зачем в первом случае пишется во временный файл… LoadCPU() должна сразу отдавать картинку, а не указатель.
Извините, но у вас грубая ошибка, при описании методики сохранения кэша в мемкеш. Единственно правильно здесь использовать функцию add (разница в том, что add добавляет данные только тогда, когда они еще не добавлены по тому же ключу)
Простите, немного Вас не понял. В приведенных мной примерах, если данные уже добавлены, то до повторного их занесения, код не дойдёт. Если же не добавлены, то добавятся. Возможно всё же я Вас не правильно понял…
Возможно подразумевается что пока вы генерировали новый контент для того чтобы пихнуть его в мемкеш, другой воркер с аналогичным запросом успел сделать это быстрее. Это проявляется при действительно большом числе клиентов, когда несколько клиентов могут послать одинаковый запрос почти одновременно
Любопытно, а что это изменит? :-) Процессор уже был нагружен процессом генерации данных. Что конкретно в данном случае изменит add?
да наверное по сути ничего толком не изменит. Просто в мемкеше НЕ обновится значение ключа при использовании add. Возможно сэкономит немного ресурсов на изменение данных внутри мемкеша.
Я, кстати, просто прояснил комментарий, но не выражал свое согласие/несогласие.
Если у меня сервер 4GB DDR2, большой проект и много всякого будет в мемкеше, то какой размер мне выбрать под мемкеш? так чтобы сайт грузился моментом и чтобы мемкеш не тупил
Или купить еще один сервер отдельно для мемкеша и выдать ему все 4 гб?
Я бы ограничился выделением памяти. В случае острой необходимости всегда успеете купить железяку. А переход на отдельный сервер не займёт много времени. ИМХО
У меня тоже 4gb памяти. Под мемкэш выделено 64мб. И всего хватает.
Смотря какие именно данные вы собираетесь кешировать.

Смотрите статистику мемкеша и подбирайте сами размер.Для этого коннектимся телнетом на порт где висит мемкеш. Команда stat (или stats вроде) выдаст статистику сколько памяти использует сейчас мемкеш. Так же учтите что при недостачи памяти метаешь начнет затирать более старые данные для записи новых.
*мемкеш. Автозамена на телефоне.
Иногда, даже Сталин заходит с телефона :)
Или купить еще один сервер отдельно для мемкеша и выдать ему все 4 гб?


В данном случае наложится оверхед дополнительный из-за «удалённости» мемкеша. В вашем случае нужно смотреть статистику и варьировать время кэширования объектов.
Гигабитный конект к второму серверу, поставить мемкеш демон с патчами от фейсбука. И все ок будет.
А что гигабиты уже как-то влияют на latency?
В любом случае гигабит лучше. А latency если верить блогу фейсбука 137микросекунд.
Понимаете, гигабит несомненно лучше… Но дело в том, что при высокой латенции, кэширование будет выгодно лишь на больших временных интервалах. Если же Вы кладёте в кэш с expire = 1, то ничего хорошего из этого не выйдет. ИМХО
Всё познаётся в сравнении… В сравнении с латенцией ОЗУ, да…
В Zend Framework очень здорово реализованы классы работы с кэшем вообще, и Memcached в частности — Zend_Cache_Backend_Memcached. Рекомендую посмотреть в эту сторону, чтобы не писать самому низкоуровневый код, который уже существует :-)
Вы абсолютно правы, но перед тем как писать для фреймворка, надо понимать, как в принципе работает система кэширования. А данный топик как-раз рассчитан на новичков.
Извините, но ваш пример кэширования изображения по-моему запутает новичка в размерах памяти для него.
Вместо этого лучше привести пример статистики мемкеша. Там наглядно видно сколько данных он взял с кэша а сколько записал в него.
~$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
stats
STAT pid 3596
STAT uptime 13155613
STAT time 1289943278
STAT version 1.2.2
STAT pointer_size 64
STAT rusage_user 4538.951666
STAT rusage_system 17874.005055
STAT curr_items 396108
STAT total_items 66974376
STAT bytes 56821023
STAT curr_connections 2
STAT total_connections 344903318
STAT connection_structures 50
STAT cmd_get 342354715
STAT cmd_set 66974376
STAT limit_maxbytes 67108864
STAT threads 1
END

Разберем по полочкам:
STAT curr_items 396108 — сейчас ключей
STAT total_items 66974376 — всего ключей c момента запуска
STAT cmd_get 342354715 — получено данных из кэша
STAT cmd_set 66974376 — записано данных в кэш
Что же выходит? Что мы записали в кэш 67млн данных а получили с него 342млн.
Так как я кэширую в основном только статистические данные (к-во сообщений и все такое) то я секономил в 5 раз меньще запросов к базе.
STAT curr_items 84 523
STAT total_items 14 680 467
STAT cmd_get 439 126 815
STAT cmd_set 14 680 509

Соотношение у нас еще круче.
Уже начали меряться эффективностью мэмкеша :)
Ну, вообще говоря 6/34 это очень сомнительный мисс-рейт. А 14/439 — вполне себе ок.
>> #Демон будет запущен от рута (В последствии желательно поменять)
>> -u nobody

Пользователь nobody далеко не root
# Run the daemon as root. The start-memcached will default to running as root i$
# -u command is present in this config file

Засмотрелся)
С моей точки зрения, применять кэширование стоит только на высоконагруженных ресурсах. Ведь каждый раз, подключаясь к серверу Memcached, вы тратите драгоценное время, что скорее всего не будет оправданным.


Кеширование нужно применять всегда и везде, где это имеет смысл. Хуже от этого не будет точно, но зато вы избавляете себя от многих потенциальных проблем в будущем. Согласитесь, что написать лишних 3-5 строчек кода сейчас намного проще, чем потом, когда вы уже подзабудете алгоритм, выискивать тонкие места и пытаться вставить туда кеширование, не задев функционала.

Другой вопрос, что использование того же memcached не всегда оправдано, когда проект не особо большой и хостится, скажем, на VDS, где памяти и так немного. Но в таких ситуациях всегда можно использовать файловый кеш — дешево и сердито :)
Не всегда это так. У меня на VDS 2GB памяти, полностью никогда не используются, а как только я подключил файловое кэширование, у меня начал сервер тормозить из-за постоянного превышения лимита на кол-во одновременно открытых файлов.
я опишу процесс установки из исходного кода, так как не все репозитории настолько богаты, как дебиановский.
#apt-get install memcached

огромное спасибо Вам за отличное описание процесса установки из исходного кода.

Для начала, Вам хватит следующего конфига:

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

Хотя возможно сейчас что то поменялось, год назад было так
Я писал про установку модуля Memcached для PHP. Демон Memcached, уже точно «во всех» репозиториях есть…
И еще вопрос. Когда стоит закрывать соединение с мемкешем? (когда надо делать $memcache->close() и надо ли)
Зависит о того, как был открыт этот коннект. Если при помощи memcache::connect, то он в любом случае будет закрыт по завершении работы скрипта.

Если же использовался memcache::pconnect, то соединение автоматически не закроется. Вместо этого при повторном запуске скрипта новое соединение не создастся, а будет использовано уже существующее из пула.

При использовании mod_php или fcgi такой коннект существует до тех пор, пока не
будет завершена работа воркера (по умолчанию 1000 запросов).

Если же php подключен к серверу через cgi, то никакого профита не будет: cgi процесс завершается сразу после выполнения скрипта и вместе с собой убивает коннект к memcache.

Использование постоянных соединений (persistent connections) дает возможность сэкономить ресурсы, но в то же время таит в себе подводные камни.

К примеру, если memcache-демон был перезапущен, то php упорно будет пытаться использовать старый коннект в течение времени, указанного в параметре retry_interval. То есть, в такой ситуации при дефолтных настройках гарантирован 15-секундный висяк всех воркеров веб-сервера.
имхо в мемкеше не помешают вложенные ключи, или метки там какие, чтобы можно было легко убить группу ключей со всеми подгруппами, типа как очистить кеш для конкретного набора ключей
Посмотрите в сторону Redis. Там при помощи Set'ов легко можно организовать тегированный кеш с теми плюшками, что вы описали
Замечания:

1) как уже заметили @ и die() лучше убрать

2) сбрасывание картинки на диск в качестве доп нагрузки на диск в принципе можно объяснить но я бы не стал такое показывать… Вдруг ваш код кто то скопирует не задумываясь…

3) pecl download memcache... unpacking... compilling... вроде можно просто sudo pecl install memcache???

4)
//Если в кэше нет объекта с ключом our_var, создадим его
//Объект our_var будет храниться 5 секунд и не будет сжат
$memcache_obj->set('our_var', date('G:i:s'), false, 5);

//Выведем закэшированные данные
echo $memcache_obj->get('our_var');
}

Тут лишний Get запрос к мемкешу. Нафига только что сгенерированные данные гонять туда-сюда по сети, и дополнительно десиреализовывать? нужно так
$var_key =date('G:i:s');
$memcache_obj->set('our_var',$var_key , false, 5);
echo $var_key;


5) Да и вообще первый пример как то некрасиво структурирован… Я бы так сделал:
<?php
    $memcache_obj = new Memcache;
    $memcache_obj->connect('127.0.0.1', 11211);
    $var_key = $memcache_obj->get('our_var');
    if(empty($var_key)){
        #если варкей не смогли найти в мемкеше, то генерируем его заново и попутно кладем в мемкеш
        $var_key=date('G:i:s');
        $memcache_obj->set('our_var',$var_key , false, 5);
    }
    echo $var_key;
    $memcache_obj->close();
?>

Код раза в полтора короче получился и логичнее

Ну и если кто еще не читал серию статей Андрея Смирнова — MustRead www.smira.ru/2008/10/16/web-caching-memcached-1/ www.smira.ru/2008/10/16/web-caching-memcached-2/ www.smira.ru/2008/10/16/web-caching-memcached-3/ www.smira.ru/2008/10/16/web-caching-memcached-4/
Последние 3 ссылки не работают (кроме номерка надо ещё дату менять), поэтому проще дать ссылку на последнею статью, в которой есть ссылки на предыдущие: www.smira.ru/2008/10/31/web-caching-memcached-6/
<?php
    if($var_key != $memcache_obj->get('our_var'))

Лучше даже так
Не понял… Это в каком месте так надо сделать?? (и зачем)
Было
<?php
    $memcache_obj = new Memcache;
    $memcache_obj->connect('127.0.0.1', 11211);
    $var_key = $memcache_obj->get('our_var');
    if(empty($var_key)){
        #если варкей не смогли найти в мемкеше, то генерируем его заново и попутно кладем в мемкеш
        $var_key=date('G:i:s');
        $memcache_obj->set('our_var',$var_key , false, 5);
    }
    echo $var_key;
    $memcache_obj->close();
?>


Стало

<?php
    $memcache_obj = new Memcache;
    $memcache_obj->connect('127.0.0.1', 11211);
    if($var_key != $memcache_obj->get('our_var'))
    {
        $var_key=date('G:i:s');
        $memcache_obj->set('our_var',$var_key , false, 5);
    }
    echo $var_key;
    $memcache_obj->close();
тогда уж что то вроде
if(! ($var_key = $memcache_obj->get('our_var')) )

Иначе будет немного не то — выскочит ошибка что обращаешься к неопределенной переменной $var_key
оператор != не производит присваивания, это оператор сравнения
Ошибки не будет, проверьте =)
Быть может стоить заменить != на !==
проверил
seriy@seriy-desktop:~$ cat phpscript.php 
<?php
    $memcache_obj = new Memcache;
    $memcache_obj->connect('127.0.0.1', 11211);
    if($var_key !== $memcache_obj->get('our_var')){
        #если варкей не смогли найти в мемкеше, то генерируем его заново и попутно кладем в мемкеш
        $var_key=date('G:i:s');
        $memcache_obj->set('our_var',$var_key , false, 5);
        print "yes";
    }
    echo $var_key;
    $memcache_obj->close();
?>

seriy@seriy-desktop:~$ php phpscript.php 
PHP Notice:  Undefined variable: var_key in /home/seriy/phpscript.php on line 4
PHP Stack trace:
PHP   1. {main}() /home/seriy/phpscript.php:0
yes22:33:23


с моим вариантом
seriy@seriy-desktop:~$ cat phpscript.php 
<?php
    $memcache_obj = new Memcache;
    $memcache_obj->connect('127.0.0.1', 11211);
    if(! ($var_key = $memcache_obj->get('our_var')) ){
        #если варкей не смогли найти в мемкеше, то генерируем его заново и попутно кладем в мемкеш
        $var_key=date('G:i:s');
        $memcache_obj->set('our_var',$var_key , false, 5);
        print "yes";
    }
    echo $var_key;
    $memcache_obj->close();
?>

seriy@seriy-desktop:~$ php phpscript.php 
yes22:34:14
Вы абсолютно правы!
PHP Notice: Undefined variable: var_key in /home/seriy/phpscript.php on line 4

Потому что переменной var_key ещё не присвоено значение, как же её можно с чем то сравнивать… Действительно, если get не найдёт заданного ключа, то вернёт false, чего мы и ждём.
Да, все верно.
Я было ошибся, но выражение можно в скобки не заключать
if(!$var_key = $memcache_obj->get('our_var')){

И вы тоже не правильному учите.

if(empty($var_key)){

Почему вы проверяете на empty()? Почему вы считаете, что если get() возвращает пустоту, то кеш протух? Если кеш протух, то get() вернёт не просто пустое значение, а false. Если вы проверяете условие на empty(), то делаете не правильное сравнение в результате которого, кеш может начать генерится всегда. Например, если вычисленное значение $var_key будет пустой строкой или массивом, вы будете постоянно генерить и записывать в кеш ни разу его не использовав.

Необходимо писать только так и никак иначе::

if($var_key === false){

Всё остальное является еретической блевотой, потому что в документации написано:
Returns the value stored in the cache or FALSE otherwise.
Кстати, у такого подхода есть очевидный недостаток. Допустим, сайт очень хорошо нагружен. Например, десятки или сотни запросов в секунду. Если во время генерации кэшируемого объекта поступает ещё один запрос, то мы этот объект генерим два раза. Вот если бы код можно было бы заключить в критическую секцию, подобного эффекта не наблюдалось бы. А всё потому, что PHP.
Самое простое в этом случае — перед генерацией нового объекта создавать в memcached новый ключ, который будет указывать на то, что один из процессов уже занимается этой задачей. Другие процессы, видя «блокировку» на объекте, либо отдадут его старое значение, либо «подвиснут», чтобы дождаться нового, либо ответят клиенту «обратись через 5 секунд». Возможны варианты.
Другие процессы, видя «блокировку» на объекте, либо отдадут его старое значение

Вот! Только проблема в том, что memcache не гарантирует надёжного хранения. Т.е. если ему вдруг понадобилось почистить память, он возьмёт и грохнет какие-то, одному ему ведомо какие, значения. В том числе и «старое» значение может внезапно пропасть. Так что если рассматривать все возможные ситуации, получится нагромождение кодокостылей. И это вместо банального synchronized (monitor) {… } (ну или lock (monitor) {… }, кому как).
либо «подвиснут»

Это каким же макаром? В PHP нет же критических секций.
либо ответят клиенту «обратись через 5 секунд»

Ну что же, запасаемся попкорном и ждём гневной реакции от юзеров.

В общем, memcache — это странный инструмент. PHP же хорош, когда надо быстро сделать сайт с посещением в 3,5 анонимуса. Если планируется, что нагрузка будет на порядки больше, то следует правильно выбирать инструмент. Но кто-то изначально неправильно выбирает инструмент, а потом городит костыли вроде memcache.
>Вот! Только проблема в том, что memcache не гарантирует надёжного хранения.

А если использовать shm_put_var()/shm_has_var() для хранения признака генерации кэшируемого значения? Там вроде не грохнет сам, память разделяемая если только кончится, нельзя будет создать признак. Полностью коллизии не исключает, но, имхо, их вероятность снижает… На практике не пробовал ещё, но кое-какие механизмы межпроцессного взаимодействия у PHP есть.

>Это каким же макаром? В PHP нет же критических секций.

Например так:
while (shm_has_var($shm_id, $key)) usleep(50000);
самый примитивный вариант, конечно, надо проследить чтобы по таймлимиту скрипт не грохнулся, а, скажем, через некоторое время плюнул на кэш и стал генерировать сам (что грозит положить весь сервер, если что-то неприятное действительно происходит, а не единичный сбой) или пользователю что-нить «приятное» сказать.

Посмотрите в сторону Memcached::cas. Но это уже не Memcache а Memcached… Так что выход есть.
Сервер ставим Memcached, а компилируем php5-memcache, интересно, правда работать будет?
# apt-cache search php5-memcache
php5-memcache — memcache extension module for PHP5

Это в репозитории Debian
pecl search memcache
memcache 3.0.5 (beta)
memcached 1.0.2 (stable)

Это в pecl.
Почему они решили назвать разные версии, разными именами, не знаю… Каюсь)))
Дело не в названии, memcache != memcached. Первый реализует 18 методов, второй — 38.
Логично, memcached старше своего товарища.
Хотя, нет. Не логично… Но и в обеих версиях есть нужные нам методы…
UFO just landed and posted this here
UFO just landed and posted this here
А на винде memcached нормально работает в денвере?
Прочитал статью и офф.доку в php, но осталось пару вопросов:

1. каким образом происходит защита данных? Если коннект к серверу происходит так легко:

$memcache_obj->connect('127.0.0.1', 11211)

без логина и пароля, то кто угодно может подключится к моему серверу

$memcache_obj->connect('my-site.com', 11211);

и считать все данные. Если я настрою в PHP хранение сессий в memcache, то злоумышленнику достаточно будет знать только идентификатор сессии, чтобы получить все данные. А ещё он сможет скопировать все данные в свою сессию и стать авторизованным пользователем.

2. Не совсем понял как будет работать скрипт если установить несколько серверов $m->addServers(...)? При выполнении метода set(), новый итем будет сохранён на всех серверах или только на текущем? Если только на текущем, значит, тяжёлые операции вычислений будут происходить тем чаще, чем больше memcache-серверов.
Sign up to leave a comment.

Articles