PHP

Кэширование на PHP

Кэширование данных - это особая технология, направленная на снижение нагрузки на сервер. Суть ее заключается в том, что динамический контент, выбранные из базы данные или данные, получаемые из сторонних источников, сохраняются на диске в виде обычного текстового файла или в памяти вашего сервера. Когда они потребуются снова, то уже нет необходимости опять выполнять тяжелый запрос к базе, обращаться к стороннему серверу или заново парсить шаблон страницы, достаточно просто прочитать эти данные из ранее сохраненного файла или из памяти. Такой подход позволяет значительно ускорить генерацию страниц и тем самым ускорить загрузку сайта, что немаловажно на нагруженных проектах. Актуальность закэшированных данных устанавливается параметром "жизни кэша", то есть интервалом времени от момента сохранения данных до момента, когда их требуется обновить. "Время жизни" определяется для каждого типа данных индивидуально. Так, например, текст статьи может больше никогда не меняться после опубликования, поэтому ее закэшированный вариант можно также никогда не обновлять, кэш с комментариями может обновляться по мере добавления комментариев, данные пузомерок Google PR и Яндекс тИЦ достаточно обновлять раз в сутки, а картинку счетчика посещений желательно перерисовывать с интервалом не реже 10 минут.

Сейчас мы попробуем написать свой модуль для работы с кэшем, основанный на файлах, чтобы понять как это все работает. Функции будут очень простые:
// Определить папку для хранения кэшированных данных
define ('CACHE_DIR',dirname(__FILE__).'/cache');
 
// Запись данных в кэш
function save_cache($file, $data) {
    if ($f=fopen(CACHE_DIR.'/'.$file,'w')) {
        fwrite($f,serialize($data));
        fclose($f);
        return true;
    }
    else {
        return false;
    }
}

С записью проблем нет, мы просто создаем файл с заданным именем и записываем в него сериализованные данные. Чтение из кэша немного сложнее, так как сперва надо будет проверять наличие файла и, если требуется, его актуальность.
// Чтение данных из кэша
function load_cache($file, $expire_time=0) {
    $fname=CACHE_DIR.'/'.$file;
    // Проверка наличия файла
    if (file_exists($fname)) {
        if ($expire_time) {
            // Проверка актуальности файла
            if ((time()-filemtime($fname))>=$expire_time) {
                return false;
            }
        }
        // Прочитать данные
        if ($f=fopen($fname,'r')) {
            $tmp=fread($f,filesize($fname));
            fclose($f);
            return (unserialize($tmp));
        }
        else {
            return false;
        }
    }
    else {
        return false;
    }
}

Как вариант, можно дополнительно сжимать данные перед записью в файл и распаковывать их после загрузки. Это допустимо в случаях, когда скорость чтения сжатых данных и последующей распаковки в памяти даст выигрыш перед чтением неупакованных данных с диска.

Функции для работы с кэшем у нас есть, можно применить их на конкретных примерах. Первый пример - чтение из базы данных. Время "жизни кэша" в этом примере равно 10 минутам (время передается в секундах).
// Запрос к базе данных
$query="SELECT `fucking_huge_data` FROM `big_slow_table`";
 
// Имя файла с закэшированными данными
$file=md5($query);
 
// Попробовать загрузить данные из кэша, актуальность = 10 минут
$res=load_cache($file, 10*60);
 
// Данные не загрузились или устарели, читаем их из базы
if ($res===false) {
    $sql_result=mysql_query($query);
    $res=array();
    while ($tmp=mysql_fetch_array($sql_result)) {
        $res[]=$tmp;
    }
    // Сохранить данные в кэш
    save_cache($file,$res);
}
// Операции с прочитанными или загруженными данными
...

Следующий пример - превращение динамической страницы в некое подобие статической. Для этого воспользуемся функций ob_start, чтобы получить содержимое страницы до отправки ее в браузер. О том, как работает эта функция, прочитайте в документации.
// Имя файла для хранения закэшированной страницы
$file=md5(getenv('REQUEST_URI'));
 
// Загрузить страницу из кэша
$content=load_cache($file);
if ($content) {
    echo $content;
    exit;
}
 
// Начать буферизацию
ob_start();
 
echo "<html>";
echo "<head>";
echo "  <title>Кэшированная страница</title>";
echo "</head>";
echo "<body>";
echo "Эти данные закэшированы ".date('d.m.Y H:i.s')."<br>";
echo "переданные параметры:";
echo "<pre>";
echo print_r($_GET,true)."</pre>";
echo "</body>";
echo "</html>";
 
// Получить страницу из буфера
$content=ob_get_contents();
// Сохранить данные в кэш
save_cache($file,$content);
 
// Отправить страницу в браузер и очистить буфер
ob_end_flush();

Имя файла кэша зависит от имени запрошенного скрипта и переданных ему методом GET параметров. Если такая страница уже запрашивалась ранее, то она должна быть в кэше на постоянном хранении. Иначе она динамически создается и записывается в кэш. Естественно, такой метод кэширования не допускается, если странице передаются параметры методом POST, но даже в этом случае все равно можно кэшировать неизменные фрагменты страницы.

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

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

Для проектов с большой нагрузкой лучше обратить внимание на nginx и Memcached, но это совершенно другая история. А для небольших сайтов вполне сгодится описанный в статье метод.
Теги:
Кэширование, nginx, Memcached
Добавлено: 16 Сентября 2013 03:01:45 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...