Кэширование директорий в memcache php. Использование Memcache при помощи PHP

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

Memcache модуль также предоставляет обработчик сессии. Более подробную информацию о Memcached можно найти на сайте memcached.org

В принципе описания установки и настройки на русском языке уже готово и с ним можно ознакомится .

Имейте ввиду сохранять в кэше можно любые типы данных: и переменные, и объекты, и массивы, НО НЕ РЕСУРСЫ!

Если какой либо объект имеет в своём составе свойство - ресурс, например соединение с базой данных, или содержит другой объект имеющий подобные свойства, то эту ситуацию нужно обрабатывать отдельно, например переопределив метод __sleep() в котором этот ресурс нужно "убить" и метод __wakeup() в котором этот ресурс нужно возродить.

На самом деле все эти методы можно подразделить на 3 группы:

  1. Работа со значениями
  2. Работа с серверами и соединениями
  3. Получение информации

Работа со значениями

Методы этой группы позволяют делать следующее:

  • Устанавливать значения
  • Удалять эти значения
  • Заменять эти значения
  • Обращаться к этим значения по ключу
  • Управлять сроком жизни значений

Пара специфичных методов для инкремента и декремента целочисленных значений.

Memcache::set()
Memcache::add()

Позволяют установить значение, задать сжатие и время жизни для этого значения. Единственное различие в поведении этих методов это то, что метод Memcache::add - вернёт FALSE, если значение с таким ключём уже установлена.

bool Memcache::add
bool Memcache::set (string $key , mixed $var [, int $flag [, int $expire ]])

  • string $key - ключ значения, используя его мы оперируем значением.
  • mixed $var - значение.
  • константы
connect("localhost", 11211, 30); $cache->add("myfirst", 1000, MEMCACHE_COMPRESSED, 15); $cache->set("mysecond", 2000, MEMCACHE_COMPRESSED, 15);

Memcache::get()

Вернёт запрошенное значение или FALSE в случае неудачи, или, если значение с таким ключём ещё не установлено.

Можно передать массив ключей значений, тогда Memcache::get тоже вернёт массив, он будет содержать найденные пары ключ-значение.

Предупреждения!

Если в имени ключа присутствует обратный слэш, то результаты работы будут непредсказуемы!

Есть у Memcached и некоторые болячки: по некоторым данным, он (иногда) подвержен ошибкам переполнения буфера , который время от времени приводит к перезаписи [частичной] данных и, следовательно, делает невозможным десериализацию структур, не забудьте проверить, если в результате получения массива или объекта Memcache::get вернёт bool, dobule или long, значит что то пошло не так.

string Memcache::get (string $key [, int &$flags ])
array Memcache::get (array $keys [, array &$flags ])

  • string $key - ключ значения, или массив ключей, значения которых нужно получить.
  • int $flag - назначение этого параметра осталось для меня тайной. Я пробовал использовать некоторые целочисленные значения, но это не дало ни какого эффекта. Да и примеры в основном даны без его использования, из чего мною сделан вывод: можно прекрасно работать и без него:)
connect("localhost", 11211, 30); $cache->add("myfirst", 1000, MEMCACHE_COMPRESSED, 15); $cache->set("mysecond", 2000, MEMCACHE_COMPRESSED, 15); print_r($cache->get(array("myfirst","mysecond")));

Результат:

Array ( => 1000 => 2000)

Memcache::delete()

Удалить значение из кэша.

Замечание:

Этот метод работает не всегда (Зависит от версии программного обеспечения)! в конце страницы указаны случаи когда Memcache::delete работает, а когда нет.

Что ж, из стабильных вариантов остаётся только обнулять ненужные значения:

$cache->set("three", NULL);

Один только минус - ключ в этом случае ни куда не девается

string Memcache::delete (string $key [, int $timeout = 0 ])

  • string $key - ключ значение, которого нужно удалить.
  • int $timeout - Этот параметр так же устарел и не поддерживается, по умолчанию равен 0 секунд. Не используйте этот параметр.

Memcache::replace()

Перезаписать существующее значение.

Memcache::replace() должна использоваться, чтобы заменить существующее значение . В случае, если значение с таким ключом не существует, Memcache::replace() возвращает FALSE. В остальном Memcache::replace() ведет себя так же, как и Memcache::set(). Также можно использовать функцию memcache_replace()

string Memcache::replace (string $key , mixed $var [, int $flag [, int $expire ]])

  • string $key - ключ, значение которого нужно заменить.
  • mixed $var - новое значение.
  • int $flag - Флаг, указвающий, использовать ли сжатие (Здесь то и нужна Zlib) для сохраняемого значения, можно использовать константы
  • int $expire - Время жизни значения(кэша). Если равно нулю, то бессрочно. Вы также можете использовать метку времени или количество секунд, начиная с текущего времени, но тогда число секунд, не может превышать 2592000 (30 дней).
connect("localhost", 11211, 30); $cache->add("one", 111, 0, 15); $cache->add("two", 222, 0, 15); $cache->add("three", 333, 0, 15); $cache->replace("three", 777); print_r($cache->get(array("one","two","three"))); Array ( => 111 => 222 => 777)

Memcache::increment()

Увеличивает значение указанного ключа на указанное значение. Если значение указанного ключа не числовое и не может быть конвертировано в число, то оно изменит свое значение на значение, указанное как второй параметр. Memcache::increment() не создает элемент, если он еще не существует.

Не советуют использовать Memcache::increment() cо значениями, которые были сжаты (установлены с флагом MEMCACHE_COMPRESSED), потому, что потом будут "сюрпризы", когда вы попытаетесь получить эти значения при помощи Memcache::get()

Лично я проверил и ни каких глюков не заметил:)

int Memcache::increment

  • string $key - ключ, значение которого нужно инкрементировать.
  • int $value - значение инкремента.
connect("localhost", 11211, 30); $cache->set("someOne", 10, 0, 5); $cache->increment("someOne", 10, 0, 5); $cache->increment("someOne", 10, 0, 5); echo $cache->get("someOne");// выведет 30

Memcache::decrement()

Уменьшает значение указанного ключа на указанное значение. Аналогично Memcache::increment(), текущее значение элемента преобразуется в числовое и после этого уменьшается

int Memcache::decrement (string $key [, int $value = 1 ])

  • string $key - ключ, значение которого нужно декрементировать.
  • int $value - значение декремента.

Memcache::flush()

Memcache::flush() делает недействительными все существующие значения. Memcache::flush() на самом деле не освобождает ресурсы памяти, а только помечает все элементы как устаревшие, и занимаемая память будет доступна для использоавания новыми значениями. Также вы можете использовать функцию memcache_flush()

bool Memcache::flush (void)

Пример ниже не выведет ничего.

connect("localhost", 11211, 30); $cache->set("someOne", 111, 0, 5); $cache->set("someTwo", 222, 0, 5); $cache->set("someTree", 333, 0, 5); $cache->flush(); //Очистит кэш print_r($cache->get(array("someOne","someTwo","someTree")));

Работа с серверами и соединениями.

Memcache может работать, как с одним сервером, так и пулом (набором) серверов.

Если вы будете использовать только один сервер для обслуживания кэша, то для инициализации работы Вам будет достаточно создать объект кэша, и использовать метод Memcache::connect

Если вы собираетесь формировать пул серверов, то тут потребуется сначала создать объект кэша, а потом методом Memcache::addServer добавить в него требуемое количество серверов.

Memcache::connect()

Устанавливает соединение с сервером Memcached. Соединение, которое было открыто с помощью Memcache::connect() будет автоматически закрыто по завершению скрипта, но Вы можете закрыть его явно используя Memcache::close(). Также вы можете использовать функцию memcache_connect ().Возвращает TRUE в случае успешного завершения или FALSE в случае возникновения ошибки.

bool Memcache::connect

connect("localhost", 11211, 30);

Memcache::pconnect()

Этот метод похож на Memcache::connect() с той лишь разницей, что он устанавливает постоянное соединение. Это соединение не закрывается по окончанию выполнения скрипта и вызовом Memcache::close(). Также вы можете использовать функцию memcache_pconnect().Возвращает Memcache object в случае успешного завершения или FALSE в случае возникновения ошибки.

mixed Memcache::pconnect (string $host [, int $port [, int $timeout ]])

  • Этот параметр может также указать другие транспорты, как unix:///path/to/memcached.sock т.е. использовать UNIX сокеты, в этом случае порт должен быть установлен в 0.
  • Установите этот параметр в 0, если предполагается использование сокетов.
  • int $timeout - Лимит в секундах, для подключения к демону. Подумайте дважды, прежде чем изменять значение по умолчанию 1 секунда - вы можете потерять все преимущества кэширования, если ваше соединение будет происходить слишком долго.
pconnect("localhost", 11211, 30);

Memcache::close()

Закрывает соединение с сервером Memcached. Эта функция не закрывает постоянные соединения, которые закрываются только во время выключения/перезагрузки веб-сервера. Также можно использовать функцию memcache_close().

bool Memcache::close (void)

Memcache::addServer()

Добавляет сервер в пул соединений. Соединение, которое было открыто с помощью Memcache::addServer() будет автоматически закрыто по завершению скрипта, так же вы можете закрыть его вручную с Memcache::close(). Можно использовать функцию memcache_add_server()

При использовании Memcache::addServer (в отличие от Memcache::connect() и Memcache::PConnect()) сетевое соединение не установается, пока оно на самом деле не потребуется. Таким образом, нет накладных расходов при добавлении большого количества серверов в пул, даже если они и не потребуются вовсе.

Пул серверов обеспечивает более высокую отказоустойчивость. Любые ошибки сокетов, или ошибки Memcached на уровне сервера (кроме ошибок памяти) вызвают перераспределение ресурсов. Такие ошибки клиента, как добавление существующего ключа не вызовет сбоев.

Замечание:

Возвращает TRUE в случае успешного завершения или FALSE в случае возникновения ошибки.

bool Memcache::addServer (string $host [, int $port = 11211 [, bool $persistent [, int $weight [, int $timeout [, int $retry_interval [, bool $status [, callable $failure_callback [, int $timeoutms ]]]]]]]])

  • string $host - Хост Memcached. Этот параметр может также указать другие транспорты, как unix:///path/to/memcached.sock т.е. использовать UNIX сокеты, в этом случае порт должен быть установлен в 0.
  • int $port - порт, на котором Memcached слушает соединения. Установите этот параметр в 0, если предполагается использование сокетов.
  • bool $persistent - Устанавливает использование постоянного соединения. По умолчанию в значение TRUE.
  • int $weight - Чем больше, тем вероятнее, что данный сервер будет выбран для хранения значений. Т.е. это своеобразный "вес" сервера в общем пуле, косвенно это ещё и предполагаемая нагрузка на сервер.
  • int $timeout - Лимит в секундах, для подключения к демону. Подумайте дважды, прежде чем изменять значение по умолчанию 1 секунда - вы можете потерять все преимущества кэширования, если ваше соединение будет происходить слишком долго.
  • string $timeoutms - не описана.

Получение информации

Memcache::getExtendedStats()

Возвращает двумерный, ассоциативный массив статистики серверов. Массив ключей типа "хост:порт" сервера. Значения содержат отдельные параметры статистики. Отказавшие сервера будет иметь значение FALSE. Вы также можете использовать функцию memcache_get_extended_stats()

Замечание:

Эта функция была добавлена в Memcache в версии 2.0.0

array Memcache::getExtendedStats ([ string $type [, int $slabid [, int $limit = 100 ]]])

А теперь прикол:

Тип статистики "cachedump" был удален из Memcached по соображениям безопасности

Пример вывода информации (вызов без параметров) :

Array ( => Array ( => 5179 => 966862 => 1336505233 => 1.4.5 => 64 => 160.302880 => 483.975109 => 6 => 358080 => 13 => 15037476 => 3501979 => 46 => 11507898 => 3529578 => 0 => 0 => 0 => 76 => 0 => 0 => 0 => 0 => 0 => 0 => 0 => 1643906080 => 11298732744 => 67108864 => 1 => 0 => 4 => 0 => 3295454 => 7623 => 3501915 => 0 => 1825755) ...)

Memcache::getServerStatus()

Возвращает статус сервера "онлайн/оффлайн". Можно также использовать функцию memcache_get_server_status()

Замечание:

Эта функция была добавлена в Memcache в версии 2.0.0

int Memcache::getServerStatus (string $host [, int $port = 11211 ])

  • string $host - Хост Memcached.
  • int $port - порт, на котором Memcached слушает соединения.

Memcache::getStats()

Возвращает ассоциативный массив со статистикой сервера. Массив ключей соответствуют статистике параметры и значения для значения параметра. Также можно использовать функцию memcache_get_stats()

array Memcache::getStats s ([ string $type [, int $slabid [, int $limit = 100 ]]])

  • string $type - Тип статистики. Допустимые значения: {reset, malloc, maps, cachedump, slabs, items, sizes}. Согласно спецификации протокола Memcached эти дополнительные аргументы "могут быть изменены для удобства разработчиков".
  • int $slabid - Используется, когда первый параметр установлен в "cachedump" определяет сервер, который будет использоваться для отладки.
  • int $limit - Используется, когда первый параметр установлен в "cachedump" ограничивает количество записей в дамп.

Memcache::getVersion()

Возвращает строку - номер версии сервера. Также вы можете использовать функцию memcache_get_version() .

string Memcache::getVersion (void)

Memcache::setCompressThreshold()

Обеспечивает автоматическое сжатие больших значений. Вы также можете использовать функцию memcache_set_compress_threshold()

Замечание:

Эта функция была добавлена в Memcache в версии 2.0.0

bool Memcache::setCompressThreshold (int $threshold [, float $min_savings ])

  • int $threshold - Минимальная длина значения, которое требуется сжимать автоматически.
  • float $min_savings - Задает коэфициент сжатия. Указанное значение должно быть между 0 и 1. Значение по умолчанию составляет 0,2 дает минимум 20% экономии сжатия.

Memcache::setServerParams()

Изменяет параметры сервера прямо во время выполнения. Вы также можете использовать функцию memcache_set_server_params() .

Возвращает TRUE в случае успешного завершения или FALSE в случае возникновения ошибки.

Замечание:

Эта функция была добавлена в Memcache в версии 2.0.0

bool Memcache::setServerParams (string $host [, int $port = 11211 [, int $timeout [, int $retry_interval = false [, bool $status [, callable $failure_callback ]]]]])

  • string $host - Хост Memcached.
  • int $port - порт, на котором Memcached слушает соединения.
  • int $timeout - Лимит в секундах, для подключения к демону. Подумайте дважды, прежде чем изменять значение по умолчанию 1 секунда - вы можете потерять все преимущества кэширования, если ваше соединение будет происходить слишком долго.
  • int $retry_interval - Устанавливает, как часто отказавший сервер будет опрашиваться, значение по умолчанию составляет 15 секунд. Установка этого параметра в -1 отключает автоматический повтор запросов.
  • bool $status - Помечает сервер как ONLINE. Установка этого параметра в FALSE и retry_interval -1 позволяет отказавшему серверу хранится в пуле активных серверов, чтобы не влиять на ключевые алгоритмы распределения. Обращения к этому серверу будут распределятся между оставшимися доступными серверами. По умолчанию значение TRUE, то есть сервер должен рассматриваться как ONLINE.
  • callable $failure_callback - Позволяет пользователю указать функцию обратного вызова для запуска при обнаружении ошибки. Функция должна принимать два параметра, имя хоста и порт вышедшего из строя сервера.

Сегодня я представлю вам небольшой фрагмент кода, который научит вас взаимодействовать с Memcache. Для того чтобы установить Mediatemple на хостинге вам необходимы данные для SSH.

Кто использует Memcache?

Memcache была разработана Danga Interactive для того, чтобы увеличить скорость работы портала LiveJournal.com. Memcache сводит время загрузки из базы данных практически до нуля, от чего растёт скорость загрузки страницы для пользователей, ускоренное управление ресурсами и т.д. На данный момент FaceBook является самым крупным пользователем сервиса Memcache. Учитывая количество людей прибывающих на FaceBook ежедневно, он чуть не завалил данный сервис - и для них выделили отдельное место.

Что такое Memcache?

Ну что, интересно стало? Memcache является универсальной системой распределения кэшированных элементов. Если в кэше ничего нет, то делается запрос к базе и результаты записываются в Memcache:

Memcache предоставляет 5 функций:

  • get() - извлекает значение по ключу
  • set() - устанавливает значение
  • add() - добавляет кэш, если его не существует
  • replace() - заменяет кэш
  • flush() - удаляет все закэшированные данные

Код

Если вы установили у себя Memcache, то вы можете начать им пользоваться. Существует мнение, что много кэша это плохо. Я с этим абсолютно не согласен! Чем быстрее грузится ваш сайт - тем лучше!

  • Первые две строки производят подключение к Memcache.
  • Далее следует скрипт подключения к базе данных.
  • Потом создаём ключ. Когда мы хотим поместить данные в Memcache, то нам необходимо передать 3 элемента для вставки: ключ, значение и время жизни кэша. Ключ необходим для получения доступа к данным. В этом примере вы можете увидеть, что я использую в качестве ключа MD5 хэш запроса.
  • Дальше мы проверяем существует ли кэш. Проверка вернёт нам true или false. Если он есть, получаем к нему доступ.
  • Если кэша нет, то мы подключаемся к базе и достаём значения. Для создания кэша используем следующее выражение: $memcache->set($key, $row, TRUE, 20); $row хранит массив того, что мы достали из базы. 20 это время жизни в кэша в секундах.

$memcache = new Memcache; $memcache->connect("127.0.0.1", 11211) or die ("Could not connect"); include("includes/connect.php"); //создать ключ, затем проверить кэш $key = md5("SELECT * FROM memcached_test where name="ashley""); $get_result = $memcache>get($key); if ($get_result) { echo $get_result["name"]; echo $get_result["username"]; echo "Data Pulled From Cache"; } else { // Получить данные из базы и создать кэш $query="SELECT * FROM memcached_test where name="ashley";"; $result = mysql_query($query); $row = mysql_fetch_array($result); print_r($row); $memcache>set($key, $row, TRUE, 20); // Хранить результат 20 секунд echo "Data Pulled from the Database"; }

Это самый простой пример использования memcache, но я надеюсь, это был хороший старт для вас.

Статья для новичков . Memcached – это такая штука для кэширования данных в оперативной памяти сервера.

В чем суть. Сайт обычно берет данные из базы, а база - это большой файл на диске, а чтение с диска априори медленнее чем из памяти. Это начинает проявляться не сразу - как только посещаемость переваливает за несколько десятков тысяч человек, а таблицы в базе вырастают до сотен тысяч строк. Более того, сама база данных по определению не эффективна. Допустим, в базе храним 1 000 000 постов, но за последние несколько дней 95% всех просмотров составляют только 100 постов. Но каждый раз нам приходится лезть в огромный файл базы и искать в нем несколько часто запрашиваемых записей - а это увеличивает нагрузку на сервер и время открытия сайта. Если же мы положим эти записи в кэш, то мы ускорим сайт и не нужно покупать мощные сервера. Одним словом, кэш - это профит !

Кэширование бывает разным. Самое простое - кэширование на файлах . Минус в том, что данные по-прежнему хранятся на диске, а это может привести к печальным последствиям . Можно кэшировать промежуточные результаты в базе (например, результаты поиска в некоторых форумных движках хранятся в базе). Ну и самое эффективное это конечно хранение в оперативной памяти. Для этого существует куча сторонних программ: Memcached, eAccelerator, APC, XCache. Кстати, MySQL тоже умеет хранить данные в своем кэше (речь не об индексах в памяти).

Вообще, пишут, что eAccelerator и XCache эффективней чем Memcached, если вы используете один сервер, так как в случае Memcached необходимо открывать TCP-соединение. Но у Memcached есть главное преимущество - это возможность разнесения данных по нескольким серверам. Например, кэш ЖЖ не уместится в памяти ни одного самого мощного сервера. Собственно, Memcached и был придуман для ЖЖ, чтобы можно было хранить данные на нескольких серверах. Но нам, новичкам, пока рано об этом думать.

Особенности Memcached

  • Простая структура хранения данных (ключ-значение).
  • Максимальное время жизни кэша - 30 дней.
  • Максимальный объем одного элемента - 1 Mb
  • Можно хранить объекты, массивы как есть. При кэшировании в файлах или в базе подобные вещи нужно загонять в строку при помощи сериализации перед сохранением.
  • Нет авторизации (пароль-логин). Т.е. если на сервере стоит Memcached, то любой пользователь на этом же сервере может получить к нему доступ.
  • Скорость доступа к данным не зависит от кол-ва элементов в кэше. Да-да, именно так.

Установка

В сети есть куча инструкций по установке, хоть на Unix, хоть на Windows. Кроме самого Memcached нужно еще поставить либу для обращения к Memcached через PHP (по аналогии с базой MySQL - кроме самой базы нужно еще поставить расширение mysql или mysqli).

Но проще всего написать хостеру. На fastvps при заказе сервера Memcached ставят по умолчанию. Главное, указать сколько памяти нужно выделить под кэш. По умолчанию это 67 Mb. У меня 4 Gb оперативы, то можно смело выделить 1 Gb. Вообще, самый простой способ оценить сколько нужно памяти под кэш, это умножить объем базы на 2. Например, базы на всех наших сайтах весят 300 Мб, то под кэш выделяем 600 Мб, а лучше брать 1 Гб, с запасом.

Memcached можно увидеть в phpinfo

Проверка

Обычно Memcached стоит на localhost и доступен через порт 11211
Смотрим статистику

connect("localhost",11211); print_r($memcache->getStats()); ?>

Результат:
Array
=> 5915
=> 583
=> 1309538445
=> 1.2.2
=> 64
=> 0.000000
=> 0.004000
=> 0
=> 0
=> 0
=> 1
=> 2
=> 2
=> 0
=> 0
=> 0
=> 0
=> 0
=> 7
=> 0
=> 1073741824
=> 1
)

Через какое-то время статистика будет выглядеть примерно так

Array
=> 5915
=> 6202245
=> 1315740107
=> 1.2.2
=> 64
=> 3.464216
=> 10.868679
=> 298
=> 17728
=> 120366
=> 1
=> 28654
=> 4
=> 133296
=> 17728
=> 124758
=> 8538
=> 0
=> 11125692
=> 103815319
=> 1073741824
=> 1
)

Оновные параметры:
=> 298 - сколько текущих элементов в кэше.
=> 17728 - сколько всего было элементов в кэше (в том числе и удаленных)
=> 120366 - сколько байт сейчас лежит в кэше
=>1073741824 - сколько байт вообще доступно под кэш (тут 1 Gb)
=> 124758 - сколько раз мы взяли данные из кэша
=> 8538 - сколько раз мы пытались взять данные из кэша, но его там не было или время жизни кэша истекло.

Отношение get_misses/get_hits показывает эффективность использования кэша. Чем оно меньше, тем эффективней используется кэш. В данном случае у нас получается, что 93% данных берется из кэша. Если у вас get_misses/get_hits=1, то значит вы делаете что-то не так (скорее всего ставите слишком малое время жизни кэша).

визуальная статистика
Код выше выводит статистику в сухом виде типа print_r()
Есть красивый вывод статистики - phpMemcachedAdmin

Это обычный php-скрипт. Открываете его у себя на сайте и получаете красивое оформление.
Настраивать ничего не нужно. По умолчанию он коннектится к localhost:11211
Скачать можно на официальной странице .

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

Допустим, у нас есть строка "test111", мы хотим ее закэшировать на 1 день. Придумаем ей какой-нибудь ключ "key1".

connect("localhost",11211); $memcache->set("key1", "test111", false, 86400); // кэшируем на 1 день. $get_result = $memcache->get("key1"); // получаем данные print_r($get_result); ?>

Усложним немного

get($key)) { $get_result = $memcache->get($key); print_r($get_result); } else { $result = "test222"; // $result – результат каких-то вычислений или выборка из БД $memcache->set($key, $result, false, 86400); echo "записали кэш на 1 сутки"; } ?>

Только со второго запуска этого скрипта мы увидим наши данные.

Еще забыл добавить про время жизни кэша. Memcached имеет ограничение на время жизни - 1 месяц. Так вот, если вы поставите 365 дней, то Memcached просто не сохранит их, при этом не выдаст ни какой ошибки. Поэтому, если ваши данные долго не меняются и вы хотите поставить максимальный срок жизни, то указывайте false
$memcache->set($key, $result, false, false);

Особенность именования ключей. Лучше в качестве ключа брать md5(ключ), потому что максимальная длина ключа 250 символов и нельзя использовать пробелы. А когда вы будет кэшировать SQL-запросы с условием, то ключ будет типа $key = "blog_id_1 WHERE activity=1 AND … AND … LIMIT 10"

Более того, в ключ нужно еще добавить какую-то константу, которая определяет, к какому сайт принадлежит кэш.
$key = md5(PROJECT."key2"); // где константа PROJECT="site.com"

Если этого не сделать, то второй сайт на том же сервере может перезаписать данные первого сайта с тем же ключом. Дело в том, что Memcached не имеет авторизации, как например база данных, поэтому приходится вот таким способом ограничивать доступ. Короче говоря, Memcached - это такая большая свалка пар ключ-значение. Поэтому все сайты хранят кэш в одном Memcached. При этом мы не можем, например, взять 10 последних записанных элементов (типа как в базе LIMIT 10). Структура Memcached необычайна проста, но за счет этого мы получаем высокую производительность.

Тегирование

Так как Memcached чрезвычайно прост (данные никак не связаны между собой - есть только связь ключ-значение), то возникают некоторые трудности на практике. Допустим, у нас есть блоги как на Хабре. Мы написали пост, сохранили его в кэш. Создали несколько пар ключ-значение: кэш под сам пост, кэш для блога, в котором отображается этот пост, кэш для прямого эфира, кэш для вывода постов пользователя в профиле этого пользователя и т.д.

$memcache->set("post_id_2211", "данные");
$memcache->set("post_blog_id_11", "данные");
$memcache->set("live_posts", "данные");
$memcache->set("post_user_id_331", "данные");

Но вдруг модератор удалил пост. Нам нужно очистить все кэши, которые связаны с этим постом.

$memcache->delete("post_id_2211");
$memcache->delete("post_blog_id_11");
$memcache->delete("live_posts");
$memcache->delete("post_user_id_331");

Короче, из-за простоты Memcached нам приходится ручками плодить лишний код. Более того, мы должны постоянно помнить какие кэши связаны с другим кэшем. Решение проблемы очень простое. Мы к каждому элементу кэша прикрепляем тег или несколько тегов.
Подробнее можно почитать .

Практика

На практике чистый Memcached никто не использует. Обычно используют какой-то класс-обертку, который поддерживает тегирование. Самым распространенным является решение ZendCache

Скачать одним архивом со всем примерами
Положим класс ZendCache в папку lib

Структура должна быть такой, если смотреть от корня
/lib/DklabCache/...
/class/Cache.class.php
/stat.php
/get_post.php
/update_post.php

Класс-обертка (или как еще называют - врапер (wrapper)) с использованием ZendCache

array(array("host" => MEMCACHED_HOST, "port" => MEMCACHED_PORT, "persistent" => false),), "compression" => false,); self::$instance = new Cache; self::$instance->memcache = new Dklab_Cache_Backend_TagEmuWrapper(new Zend_Cache_Backend_Memcached($aConfigMem)); } else { return NULL; } } return self::$instance; } public function get($key) { return $this->memcache->load($key); } public function set($key, $value, $tags=array(), $timeLife=false) { return $this->memcache->save($value, $key, $tags, $timeLife); } public function delete($key) { $this->memcache->remove($key); } public function clean($cMode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { return $this->memcache->clean($cMode,$tags); } public function __construct() { } public function __clone() { } } ?>

Это у нас класс-синглтон, говоря простыми словами, при первом вызове создается экземпляр класса (читай, подключение к Memcached) и используется при следующем вызове. Таким образом, в пределах одного скрипта мы не плодим лишние подключения и экономим ресурсы.

Константа CACHE_USE прописывается отдельно в конфиге. С помощью нее можно включать/выключать кэширование.

Параметр "compression" => false означает, что мы не сжимаем данные в кэше. Сжатие нужно для экономии места в памяти, но сжатие требует некоторого времени. Поэтому, если для вас не критичен объем памяти, то сжатее отключаем.

Параметр "persistent" => false означает выключение постоянного соединения (по аналогии с mysql_pconnect())

Кстати говоря, тут видно как использовать несколько серверов. Если у нас 1 сервер

"servers" => array(array("host" => "localhost", "port" => 11211, "persistent" => false),)

Например у нас 3 сервера Memcached

"servers" => array(array("host" => "11.33.45.11", "port" => 11211, "persistent" => false), array("host" => "11.33.45.12", "port" => 11211, "persistent" => false), array("host" => "11.33.45.13", "port" => 11211, "persistent" => false),)

По-хорошему, подобные вещи нужно вынести из класса в конфиг.

Теперь подключаем этот класс в скрипт, в котором мы хотим что-то кэшировать
Допустим скрипт вывода поста (в архиве это get_post.php)
Примеры конечно не самые лучшие, но лучше так, чем совсем ничего.
Работу с базой я специально отключил, что бы у вас было меньше проблем.

get($key_cache))){ echo "данные взяли из кэша"; return $data; } else { // находим пост в базе (выполняем select) //$data = $DB->selectRow("SELECT * FROM post WHERE id=?d", $postID); // для упрощения примера беем готовый массив (пост привязан к блогу с ID=3) $data = array("id"=>$postID, "blog_id"=>3, "title"=>"Новость 111", "text"=>"какой-то текст"); if (!empty($data)) { if (isset($Cache)) { $Cache->set($key_cache, $data, array("post_update", "post_update_".$postID, "post_blog_".$data["blog_id"]), 3600*24*10); echo "сохранили данные в кэш"; } return $data; } else return null; } } $postID = 25; $post = get_post($postID); print_r($post); ?> Хотим взять пост с id=25. При первом вызове вы должны увидеть надпись "сохранили данные в кэш". При повторных вызовах вы увидите надпись "данные взяли из кэша". Теперь попробуем обновить пост (запускаем скрипт update_post.php) query("UPDATE post SET blog_id =?d, title=?, text=? WHERE id=?d", $blogID, $title, $text, $postID); // чистим теги, связанные с эим постом $Cache = Cache::getInstance(); if (isset($Cache)) { $Cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("post_update", "post_update_".$postID, "post_blog_".$blogID)); } return true; } $postID = 25; update_post($postID, 3, "test", "test test"); ?>

После чего запускаем скрипт get_post.php и видим, что данных в кэше не было и мы снова сохранили их туда: "сохранили данные в кэш".

На самом деле самое сложное в кэшировании, это простановка правильных тегов . Чтобы при обновлении поста обновилась данные, в которых лежит этот пост.

На примере выше это были теги

  • "post_update_".$postID - сам пост (обычно страница вывода поста)
  • "post_blog_".$blogID - страница блога, где выводится список постов этого блога
  • "post_update" - тег связанный с главной страницей или списком самых лучших постов

Dog-pile эффект

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

выглядит это примерно как-то так:)

Решение проблемы описано .
Код выше не предусматривает защиты от dog-pile эффекта. В моем случае нет такой высокой посещаемости и долгих запросов.

Любой более или менее развивающийся web проект рано или поздно встречается с задачами, которые лучше всего решать с помощью кэширования. Например, сохранение в какое-либо хранилище агрегированных данных, подсчет и(или) получение которых занимает много времени. Существует несколько вариантов кэширования данных: сохранение данных на диск или в оперативную память. У каждого варианта есть свои плюсы и минусы. Сохранение на диск — медленное, запись происходит последовательно с использование блокировок, количество которых обычно ограниченно оперативной системой. Но данные сохраняются навсегда и будут доступны после перезагрузки сервера. Оперативная память же, напротив, быстрая, без блокировок, но не сохраняется в случае перезагрузки. Хранить в ней не продублированные данные опасно. Если с сохранением на диск все понятно и просто, то работа с памятью более сложна. Существуют приложения, которые берут на себя эту задачу. Одно из которых Memcached. В этой статье мы поговорим о нем.

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

В Php существует две версии библиотек: php-memcache и php-memcached. Первая старая и больше не развивается. Вторая новая и имеет больший набор функции. Различий в них больше и какую использовать решать вам. В данной статье будет использоваться php-memcached.

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 require_once __DIR__. "/func.php" ; if (! extension_loaded ("memcached" ) ) { die ("Memcached extension not installed" ) ; } $USE_CACHE_PROTECTION = true ; $AUTH_MAX_ATTEMPTS = 5 ; $AUTH_LOGIN = "demo" ; $AUTH_PASSWORD = "demo" ; $error = array () ; if (IS_POST() ) { $login = _POST("username" ) ; $password = _POST("password" ) ; if (! $login ) { $error [ "username" ] = "required" ; } if (! $password ) { $error [ "password" ] = "required" ; } // INIT MEMCACHE if ($USE_CACHE_PROTECTION ) { $cache = new Memcached() ; $cache -> addServer ("127.0.0.1" , 11211 ) ; $cacheKey = "prefix::login-protect-" . $login ; } //check cache protection if ($USE_CACHE_PROTECTION && ! $error ) { $attempts = $cache -> get ($cacheKey ) ; // CHECK FAILD ATTEMPTS if ($attempts >= $AUTH_MAX_ATTEMPTS ) { $error [ "global" ] = "max_attempts" ; } } //check auth if (! $error ) { //TODO: use db bro if ($login == $AUTH_LOGIN && $password == $AUTH_PASSWORD ) { // CLEAR DATA ON SUCCESS AUTH if ($USE_CACHE_PROTECTION ) { $cache -> delete ($cacheKey ) ; } stopAndRedirect("success.php" ) ; } if ($USE_CACHE_PROTECTION ) { //INCREMENT ATTEMPTS if (! $cache -> increment ($cacheKey ) ) { $cache -> set ($cacheKey , 1 , 60 ) ; } } $error [ "global" ] = "auth_faild" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

Демо использование memcached в php

Авторизация

Как видно из кода все предельно просто. Создается уникальный ключ, состоящий из префикса сайта, названия задачи и введенного логина. Далее читаем, наращиваем счетчик, удаляем. Стоит обратить внимание на участок кода, где скрипт наращивает счетчик неудачных попыток авторизации. Метод increment возвращает false в случае отсутствия записи в memcached. Поэтому, в случае неудачи мы устанавливаем запись со значением 1 ровно на 60 секунд.

Стоит заострить внимание на том, что мы используем метод increment , а не set(getted_value+1) . Дело в том метод set не дает возможности правильно увеличивать счетчик, так как в между промежутком чтения и записи, данные могут измениться. Например, в это же время другой пользователь будет тоже подбирать пароль. Метод increment же гарантированно увеличит счетчик правильно!

В результате применения информации, данной в этой статье, вы сможете установить и настроить сервер Memcached и нужное расширение php для взаимодействия с ним, а также сервис анализа и статистики phpMemcachedAdmin

Установка будет происходить под Debian с сервером бекенда . Если у вас рабочая связка + php5-fpm, просто заменяйте в командах apache2 на php5-fpm
Рабочий инструментарий: файловый менеджер для подключения по со встроенным редактором или putty.

Теперь допустим, у нас на этом хостинге уже есть сайт http://example.com

Для начала, нужно определить, как сконфигурирован PHP (вдруг Memcached уже установлен?)). Для этого в корне сайта временно создадим файл info.php с кодом

Теперь заглянем в него http://example.com/info.php

Если видим, что есть похожая запись, значит, всё в порядке, нужное расширение php установлено.
Если нет, значит, придётся поработать.

Как установить сервер Memcached

Перед установкой нужно обновить репозиторий

Apt-get update && apt-get upgrade

Теперь установим memcached и модуль php под него

Apt-get install memcached php5-memcache

Идёт установка. После её завершения проверяем, что получилось

В результате мы увидим что-то навроде

Tcp 0 0 *:11211 *:* LISTEN 13053/memcached

Теперь нужно перезагрузить Apache или php-fpm

Service apache2 restart

Настройка Memcached

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

Закрываем Memcached от доступа извне

Как вы видите, memcached висит на 11211 порту по умолчанию. Так как в memcached нет встроенных механизмов аутентификации, то выходит, что любой может подсоединиться извне и использовать его в своих целях. Чтобы избежать этого, вы можете либо закрыть порт 11211 с помощью фаервола, либо сконфигурировать memcached-сервер, чтобы он мог использоваться только с локального хоста. Ниже описан последний способ.

  1. Добавляем строку -l 127.0.0.1
  2. Перезагружаем memcached

    Service memcached restart

    /etc/init.d/memcached restart

И снова проверяем

Netstat -tap | grep memcached

В итоге мы должны увидеть подобное

Tcp 0 0 localhost.localdo:11211 *:* LISTEN 13092/memcached

Также, после всех изменений не забудьте перезапустить Apache

Service apache2 restart

Увеличиваем объём рабочей памяти Memcached

По умолчанию, в Memcached отведено на использование 64 мегабайта оперативной памяти. Я увеличу до 1024, вы исходите из параметров вашего сервера

  1. Открываем конфигурационный файл vi /etc/memcached.conf
  2. Находим значение -m 64
  3. Меняем его на -m 1024
  4. Перезапускаем memcached service memcached restart

Хранение сессий PHP в Memcached

Если одним сервером или кластером Memcached пользуются несколько разных сайтов, то они могут перехватывать сессии друг друга и получать доступ к аккаунтам их пользователей, что представляет собой потенциальную опасность

Можно ускорить php, перенеся хранилище сессий из hdd в оперативную память с помощью memcached

Вы должны знать, какое расширение php вы используете, memcache или memcached . , а уточнить, что стоит у вас, можно с помощью phpinfo(), тут есть тонкость в настройках, будьте внимательны

Способ для расширения memcache

  1. Открыть /etc/php5/mods-available/memcache.ini
  2. Добавить session.save_handler = memcache session.save_path = "tcp://127.0.0.1:11211"

Способ для расширения memcacheD

  1. Открыть /etc/php5/mods-available/memcached.ini
  2. Добавить session.save_handler = memcached session.save_path = "127.0.0.1:11211"
  3. Перезагрузить apache service apache2 restart

Проверка phpinfo

Теперь нужно проверить, что имеем на выходе. Для этого, открываем страницу с phpinfo() и ищем в коде session.save_path в столбце с локальными значениями. В значении должно быть tcp://127.0.0.1:11211 или 127.0.0.1:11211 ..conf), а ищем
php_admin_value session.save_path

Кстати, также вы можете воспользоваться директивой php_value , чтобы настраивать не весь сервер глобально, а только конкретные сайты. Для этого, вы можете отредактировать.htaccess в корне вашего сайта

Php_value session.save_handler "memcache" php_value session.save_path "tcp://127.0.0.1:11211"

Готовый рабочий конфигурационный файл Memcached

Вы также можете настроить всё под свои потребности, конфигурационный файл содержит подробные комментарии:

# memcached default config file # 2003 - Jay Bonci < > # This configuration file is read by the start-memcached script provided as # part of the Debian GNU/Linux distribution. # Run memcached as a daemon. This command is implied, and is not needed for the # daemon to run. See the README.Debian that comes with this package for more # information. -d # Log memcached"s output to /var/log/memcached logfile /var/log/memcached.log # Be verbose # -v # Be even more verbose (print client commands as well) # -vv # Start with a cap of 64 megs of memory. It"s reasonable, and the daemon default # Note that the daemon will grow to this size, but does not start out holding this much # memory # Объём памяти в мегабайтах, который отведён для кеширования. По умолчанию, 64 мегабайта. #-m 64 -m 1024 # Порт, на котором будет висеть Memcached, по умолчанию 11211 -p 11211 # Run the daemon as root. The start-memcached will default to running as root if no # -u command is present in this config file -u memcache # Specify which IP address to listen on. The default is to listen on all IP addresses # This parameter is one of the only security measures that memcached has, so make sure # it"s listening on a firewalled interface. -l 127.0.0.1 # Limit the number of simultaneous incoming connections. The daemon default is 1024 # # -c 1024 # Lock down all paged memory. Consult with the README and homepage before you do this # -k # Return error when memory is exhausted (rather than removing items) # -M # Maximize core file limit # -r

Установка расширения memcache

Ниже пошагово указаны команды, с помощью которых вы сможете установить php-расширение memcache из консоли вручную

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

Перезагрузим Apache, чтобы изменения вступили в силу

Service apache2 restart

Как проверить работу Memcached

Создадим в корне сайта файлик memcachetest.php и запишем туда следующий код

connect("localhost", 11211) or exit("Невозможно подключиться к серверу Memcached"); $version = $memcache->getVersion(); echo "Server"s version: ".$version."
\n"; $tmp_object = new stdClass; $tmp_object->str_attr = "test"; $tmp_object->int_attr = 123; $memcache->set("key", $tmp_object, false, 10) or die ("Не получилось оставить запись в Memcached"); echo "Записываем данные в кеш Memcached (данные будут храниться 10 секунд)
\n"; $get_result = $memcache->get("key"); echo "Данные, записанные в Memcached:
\n"; var_dump($get_result); ?>

Теперь осталось проверить результаты: http://example.com/memcachetest.php
Если всё сделали правильно, увидите что-то навроде

phpMemcachedAdmin — мониторинг, статистика и управление Memcached

phpMemcachedAdmin — это программное обеспечение, предоставляющее веб-интерфейс для мониторинга и управления сервисом Memcached на Linux. Позволяет видеть в реальном времени статистику для всех поддерживаемых сервером операций: get, set, delete, increment, decrement, reclaimed, evictions, cas, а также серверную статистику: сеть, позиции, рабочую версию вкупе с чартами и внутренней серверной конфигурацией.

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

Тут можно пойти несколькими путями: выделить отдельный домен или поддомен под работу этого сервиса. А можно сделать поддиректорию в рабочем сайте и поставить пароль на неё или . Я опишу последний способ с установкой пароля на папку как более простой.

Итак, допустим, у нас есть сайт https://сайт

Cd /var/www/сайт

Создаём директорию и переходим в неё

Mkdir phpMemcachedAdmin && cd phpMemcachedAdmin

Скачиваем последнюю версию дистрибутива

Wget http://phpmemcacheadmin.googlecode.com/files/phpMemcachedAdmin-1.2.2-r262.tar.gz

Распаковываем и удаляем архив с дистрибутивом

Tar -xvzf phpMemcachedAdmin-1.2.2-r262.tar.gz && rm -rf phpMemcachedAdmin-1.2.2-r262.tar.gz

Рекурсивно выставляем нужные права доступа в текущей директории

Find ./ -type f -exec chmod 0644 {} \; find ./ -type d -exec chmod 0755 {} \;

AuthType Basic AuthName "Private Area" AuthUserFile .htpasswd Require user memcachedmanager

Создаём.htpasswd

Htpasswd -c .htpasswd memcachedmanager

Вводите свой пароль.

На этом всё. Логин на каталог memcachedmanager , если вы не меняли его. Открываете https://сайт/phpMemcachedAdmin (домен меняете на свой), вводите логин/пароль и пользуетесь

В заключение

Рекомендую ознакомиться со статьёй про memcached . В ней раскрыта общая информация о нём и некоторые тонкости работы.
На этом всё. Если что непонятно, задавайте вопросы в комментариях

Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: