Пользователь
0,0
рейтинг
16 ноября 2010 в 20:15

Разработка → Memcached и PHP ликбез из песочницы

PHP*
В интернете достаточно много информации на данную тему, но, несмотря на это, многие обходят её стороной. Цель данного поста, разъяснить на пальцах основы взаимодействия с Memcached.

Что такое Memcache и какое отношение он имеет к PHP?


Memcache разработан для кэширования данных, генерация которых требует большого количества ресурсов. Такого рода данные могут содержать что угодно, начиная с результатов запроса к базе данных и заканчивая тяжеловесным куском шаблона. Memcached не входит в базовый набор модулей, поставляемых с PHP, однако он доступен в репозитории pecl.

Установка и настройка


В качестве рассматриваемого дистрибутива я решил использовать Debian, потому как он наиболее часто используется при создании web-серверов. Модуль Memcached для PHP доступен в репозитории уже скомпилированным (php5-memcached), но я опишу процесс установки из исходного кода, так как не все репозитории настолько богаты, как дебиановский.

Устанавливаем сервер Memcached


#apt-get install memcached
Для начала, Вам хватит следующего конфига:
#/etc/memcached.conf
#Memcached будет работать, как демон
-d
#Лог будет складывать туда
logfile /var/log/memcached.log
#Отведём 256 мегабайт ОЗУ под хранилище
-m 256
#Слушать будет этот порт
-p 11211
#В последствии желательно поменять
-u nobody
#Слушаем localhost
-l 127.0.0.1

#/etc/init.d/memcached restart

Проверяем

# netstat -tap | grep memcached
tcp    0   0 localhost:11211     *:*           LISTEN   13036/memcached

Компилируем и устанавливаем модуль для PHP


apt-get install php5-dev libmemcache-dev
 
pecl download memcache
tar xzvf memcache-2.2.6.tgz
cd memcache-2.2.6/
phpize && ./configure --enable-memcache && make
cp modules/memcache.so /usr/lib/php5/20060613/
 
echo 'extension=memcache.so' >> /etc/php5/apache2/php.ini
/etc/init.d/apache2 restart

Вот и всё! Совсем не сложно.

Примеры использования


1. Базовые операции


  1.     <?php
  2.     //Создаём новый объект. Также можно писать и в процедурном стиле
  3.     $memcache_obj = new Memcache;
  4.  
  5.     //Соединяемся с нашим сервером
  6.     $memcache_obj->connect('127.0.0.1', 11211) or die(«Could not connect»);
  7.  
  8.     //Попытаемся получить объект с ключом our_var
  9.     $var_key = @$memcache_obj->get('our_var');
  10.  
  11.     if(!empty($var_key))
  12.     {
  13.         //Если объект закэширован, выводим его значение
  14.         echo $var_key;
  15.     }
  16.  
  17.     else
  18.     {
  19.         //Если в кэше нет объекта с ключом our_var, создадим его
  20.         //Объект our_var будет храниться 5 секунд и не будет сжат
  21.         $memcache_obj->set('our_var', date('G:i:s'), false, 5);
  22.  
  23.         //Выведем закэшированные данные
  24.         echo $memcache_obj->get('our_var');
  25.     }
  26.  
  27.     //Закрываем соединение с сервером Memcached
  28.     $memcache_obj->close();
  29.     ?>

В результате выполнения этого кода каждый раз будет выводиться время с точностью до секунд. Однако обновляться оно будет раз в 5 секунд, пока не очистится кэш. В данном примере проиллюстрированы самые простые операции, но в производительности мы скорее потеряем, чем выиграем. Ведь нам каждый раз придётся подключаться к серверу…

2. Повышаем производительность



2.1 С кэшированием

  1. <?php
  2. function LoadCPU()
  3. {
  4. //Функция, которая должна зугрузить процессор
  5.  
  6. //Создадим изображение 800x600
  7. $image = imagecreate(800600);
  8.  
  9. //Белый фоновый цвет
  10. $color = imagecolorallocate($image, 255255255);
  11.  
  12. //Чёрный
  13. $color2 = imagecolorallocate($image, 000);
  14.  
  15. for ($i = 0; $i < 10000; $i++) {
  16. //Расставим 10 000 точек в случайном порядке
  17. imagesetpixel($image, rand(0800)rand(0,600), $color2);
  18. }
  19.  
  20. //Выбрасываем указатель
  21. return $image;
  22. }
  23.  
  24.  
  25. //Создаём новый объект Memcache
  26. $memcache_obj = new Memcache;
  27.  
  28. //Соединяемся с нашим сервером
  29. $memcache_obj->connect('127.0.0.1'11211) or die("Could not connect");
  30.  
  31. //Попытаемся получить объект с ключом image
  32. $image_bin = @$memcache_obj->get('image');
  33.  
  34. if(empty($image_bin)) {
  35.  
  36. //Если в кэше нет картинки, сгенерируем её и закэшируем
  37. imagepng(LoadCPU(),getcwd().'/tmp.png',9);
  38. $image_bin = file_get_contents(getcwd().'/tmp.png');
  39. unlink(getcwd().'/tmp.png');
  40. $memcache_obj->set('image', $image_bin, false30);
  41. }
  42.  
  43. //Выведем картинку из кэша
  44. header('Content-type: image/png');
  45. echo $image_bin;
  46.  
  47. //Закрываем соединение с сервером Memcached
  48. $memcache_obj->close();
  49. ?>

В данном примере приведена функция, которая создаёт изображение размером 800x600 и расставляет на нём 10 000 точек. Один раз, сгенерировав такое изображение, в дальнейшем мы лишь выводим его на экран, не генерируя заново.

2.2 Без кэширования

  1. <?php
  2. function LoadCPU()
  3. {
  4. //Функция, которая должна загрузить процессор
  5.  
  6. //Создадим изображение 800x600
  7. $image = imagecreate(800, 600);
  8.  
  9. //Белый фоновый цвет
  10. $color = imagecolorallocate($image, 255, 255, 255);
  11.  
  12. //Чёрный
  13. $color2 = imagecolorallocate($image, 0, 0, 0);
  14.  
  15. for ($i = 0; $i < 10000; $i++) {
  16. //Расставим 10 000 точек в случайном порядке
  17. imagesetpixel($image, rand(0, 800), rand(0,600), $color2);
  18. }
  19.  
  20. //Выбрасываем указатель
  21. return $image;
  22. }
  23.  
  24. //Выводим изображение, не кэшируя
  25. header('Content-type: image/png');
  26. imagepng(LoadCPU(),'',9);
  27. ?>

Тут всё гораздо проще и привычней: генерируем изображение каждый раз заново.

Результаты

Я протестировал оба скрипта на производительность. Одна и та же машина в первом случае выдала 460 ответов в секунду, а во втором лишь 10. Чего и следовало ожидать.
memcache


Ещё несколько полезных функций



addServer — в случае, если у вас в распоряжении несколько кэширующих серверов, вы можете создать некий кластер, добавляя сервера в пул. Следует обратить внимание на параметр weight. Он указывает на то, сколько памяти вам будет доступно на конкретном сервере.
delete — из названия понятно, что данный метод удаляет из кэша объект с заданным ключом.
replace — заменяет значение объекта с заданным ключом. Используйте в случае, если Вам понадобится изменить содержимое объекта, раньше чем истечёт время его жизни.

Итог


С моей точки зрения, применять кэширование стоит только на высоконагруженных ресурсах. Ведь каждый раз, подключаясь к серверу Memcached, вы тратите драгоценное время, что скорее всего не будет оправданным. Что касается больших проектов, лучше сразу написать больше строк кода, чем потом делать это в попыхах, с мыслью о том, что ваш сервис лежит. Также не стоит забывать о расходовании памяти! Учтите, что положив 300 мегабайт в кэш, вы отняли у себя 300 мегабайт ОЗУ...
В завершение хочу сказать, что данная статья не раскрывает все прелести технологии, однако я надеюсь, что она стимулирует Вас к самосовершенствованию. Спасибо за прочтение, многоуважаемый %username%!

UPD: Ещё один интересный момент. Memcached, есть PHP API к libmemcached. А Memcache, библиотека для php, не использующая libmemcached.
Владимир Цванг @vtsvang
карма
12,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (87)

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

    мне кажется вы заблуждаетесь…
    • +3
      > мне кажется вы заблуждаетесь…
      Таки все?
      • –3
        да.
        ибо дебиан _НЕ_ уступает набору софта в стандартной поставке только centOS
        в остальном там общее кол-во пакетов не большое.
        • +4
          Я не спорю. Данный топик вообще не посвящён Операционным Системам. В некоторых системах нет скомпилированного memcached модуля, я нашёл предлог, чтобы собрать его из исходников. Что я написал не правильно?
        • 0
          Debian содержит меньшее количество пакетов потому, что Debian борется за «чистоту» рядов. Если начать учитывать всякую проприетарщину в non-free, то будет сравнимо, если не больше. Впрочем если посчитать еще и поддерживаемые архитектуры, то Debian явно в лидерах
      • +1
        Самое богацтво, по-моему, в Arch AUR — вот там есть почти всё, что только может быть душе угодно. Там даже виндовые программы в вайновской обвязке (например foobar2000) и досовские «со встроенным», так сказать, dosbox-ом (например я там неимоверно продвинутую версию Dune 2 нашёл, которую больше нигде не видел). Хорошо это или плохо — однозначно не скажешь — не совсем труъ конечно, зато страшно удобно и очень приятно иногда.
  • +8
    А можно код отформатировать, чтобы с отступами был? А еще избавиться, наконец, от "@" и «or die()».
    • 0
      Вы предлагаете в таком мануале «для новичков» бросать исключения?
      • +5
        1. нормально подсветить код
        2. не учить новичков плохой практике.
        • 0
          А чем Вам не понравилась данная практика?
          • +1
            я говорю не про статью в целом, а про «сокрытие ошибок».
      • +3
        В чисто академических целях можно вообще не отлавливать исключительные ситуации. В мануале для новичков лучше просто поубирать "@" и «or die()». Так или иначе, ни в коем случае нельзя учить этим пользоваться, надо сразу приучать к дисциплине и хорошему коду.
        • –5
          Да впрочем вопрос из ничего. Кто как хочет, так и делает. Этому не научишь, не так ли?
          • +3
            Почему же? Нужно просто показывать положительный пример.
          • 0
            Я не знаком с мемкэшем и мне действительно было бы удобнее видеть какой-нибудь практический пример близкий к живому. Не то, чтобы я не понимал принципов работы или не мог разобрать код без табуляции, но, все же очень хотелось бы видеть «чистую» работу.

            P.S статья действительно в самый раз для тех, кто впервые знакомится с этой «системой» кэширования. За это Вам спасибо
  • +10
    Сомнительная затея, помещать картинку на диск во временный файл, а потом читать оттуда для кеширования в памяти.
    Лучше использовать ob_start() и ob_get_flush() для одновременной отдачи картинки клиенту и получения её содержимого.
    • 0
      Надо же было нагрузить ещё и диск:)
    • –1
      Я вот тоже задумался, зачем в первом случае пишется во временный файл… LoadCPU() должна сразу отдавать картинку, а не указатель.
  • 0
    Извините, но у вас грубая ошибка, при описании методики сохранения кэша в мемкеш. Единственно правильно здесь использовать функцию add (разница в том, что add добавляет данные только тогда, когда они еще не добавлены по тому же ключу)
    • 0
      Простите, немного Вас не понял. В приведенных мной примерах, если данные уже добавлены, то до повторного их занесения, код не дойдёт. Если же не добавлены, то добавятся. Возможно всё же я Вас не правильно понял…
      • 0
        *мною
      • +4
        Возможно подразумевается что пока вы генерировали новый контент для того чтобы пихнуть его в мемкеш, другой воркер с аналогичным запросом успел сделать это быстрее. Это проявляется при действительно большом числе клиентов, когда несколько клиентов могут послать одинаковый запрос почти одновременно
        • 0
          Любопытно, а что это изменит? :-) Процессор уже был нагружен процессом генерации данных. Что конкретно в данном случае изменит add?
          • +1
            да наверное по сути ничего толком не изменит. Просто в мемкеше НЕ обновится значение ключа при использовании add. Возможно сэкономит немного ресурсов на изменение данных внутри мемкеша.
            Я, кстати, просто прояснил комментарий, но не выражал свое согласие/несогласие.
            • 0
              Ок :-)
  • 0
    Если у меня сервер 4GB DDR2, большой проект и много всякого будет в мемкеше, то какой размер мне выбрать под мемкеш? так чтобы сайт грузился моментом и чтобы мемкеш не тупил
    Или купить еще один сервер отдельно для мемкеша и выдать ему все 4 гб?
    • 0
      Я бы ограничился выделением памяти. В случае острой необходимости всегда успеете купить железяку. А переход на отдельный сервер не займёт много времени. ИМХО
    • +2
      У меня тоже 4gb памяти. Под мемкэш выделено 64мб. И всего хватает.
      Смотря какие именно данные вы собираетесь кешировать.

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


      В данном случае наложится оверхед дополнительный из-за «удалённости» мемкеша. В вашем случае нужно смотреть статистику и варьировать время кэширования объектов.
      • 0
        Гигабитный конект к второму серверу, поставить мемкеш демон с патчами от фейсбука. И все ок будет.
        • +1
          А что гигабиты уже как-то влияют на latency?
          • 0
            В любом случае гигабит лучше. А latency если верить блогу фейсбука 137микросекунд.
            • 0
              Понимаете, гигабит несомненно лучше… Но дело в том, что при высокой латенции, кэширование будет выгодно лишь на больших временных интервалах. Если же Вы кладёте в кэш с expire = 1, то ничего хорошего из этого не выйдет. ИМХО
              • 0
                137 это высокая латениция?
                • 0
                  Всё познаётся в сравнении… В сравнении с латенцией ОЗУ, да…
  • 0
    В Zend Framework очень здорово реализованы классы работы с кэшем вообще, и Memcached в частности — Zend_Cache_Backend_Memcached. Рекомендую посмотреть в эту сторону, чтобы не писать самому низкоуровневый код, который уже существует :-)
    • +1
      Вы абсолютно правы, но перед тем как писать для фреймворка, надо понимать, как в принципе работает система кэширования. А данный топик как-раз рассчитан на новичков.
      • +1
        Извините, но ваш пример кэширования изображения по-моему запутает новичка в размерах памяти для него.
        Вместо этого лучше привести пример статистики мемкеша. Там наглядно видно сколько данных он взял с кэша а сколько записал в него.
  • +2
    ~$ 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 раз меньще запросов к базе.
    • 0
      STAT curr_items 84 523
      STAT total_items 14 680 467
      STAT cmd_get 439 126 815
      STAT cmd_set 14 680 509

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

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

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


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

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

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

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

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

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

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

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

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

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

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

    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/
    • +2
      Последние 3 ссылки не работают (кроме номерка надо ещё дату менять), поэтому проще дать ссылку на последнею статью, в которой есть ссылки на предыдущие: www.smira.ru/2008/10/31/web-caching-memcached-6/
    • 0
      <?php
          if($var_key != $memcache_obj->get('our_var'))
      

      Лучше даже так
      • 0
        Не понял… Это в каком месте так надо сделать?? (и зачем)
        • +1
          Было
          <?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();
          • +1
            тогда уж что то вроде
            if(! ($var_key = $memcache_obj->get('our_var')) )

            Иначе будет немного не то — выскочит ошибка что обращаешься к неопределенной переменной $var_key
            оператор != не производит присваивания, это оператор сравнения
            • 0
              Ошибки не будет, проверьте =)
              • 0
                Быть может стоить заменить != на !==
              • +1
                проверил
                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
                
                • 0
                  Вы абсолютно правы!
                  PHP Notice: Undefined variable: var_key in /home/seriy/phpscript.php on line 4

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

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

      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.
  • –2
    Кстати, у такого подхода есть очевидный недостаток. Допустим, сайт очень хорошо нагружен. Например, десятки или сотни запросов в секунду. Если во время генерации кэшируемого объекта поступает ещё один запрос, то мы этот объект генерим два раза. Вот если бы код можно было бы заключить в критическую секцию, подобного эффекта не наблюдалось бы. А всё потому, что PHP.
    • +1
      Самое простое в этом случае — перед генерацией нового объекта создавать в memcached новый ключ, который будет указывать на то, что один из процессов уже занимается этой задачей. Другие процессы, видя «блокировку» на объекте, либо отдадут его старое значение, либо «подвиснут», чтобы дождаться нового, либо ответят клиенту «обратись через 5 секунд». Возможны варианты.
      • 0
        Другие процессы, видя «блокировку» на объекте, либо отдадут его старое значение

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

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

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

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

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

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

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

    • 0
      Посмотрите в сторону Memcached::cas. Но это уже не Memcache а Memcached… Так что выход есть.
  • 0
    Сервер ставим Memcached, а компилируем php5-memcache, интересно, правда работать будет?
    • 0
      # 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.
      Почему они решили назвать разные версии, разными именами, не знаю… Каюсь)))
      • 0
        Дело не в названии, memcache != memcached. Первый реализует 18 методов, второй — 38.
        • 0
          Логично, memcached старше своего товарища.
          • 0
            Хотя, нет. Не логично… Но и в обеих версиях есть нужные нам методы…
  • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    А на винде memcached нормально работает в денвере?
  • 0
    Прочитал статью и офф.доку в php, но осталось пару вопросов:

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

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

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

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

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

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

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.