Blog. Just Blog

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

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

Сейчас мы попробуем написать свой модуль для работы с кэшем, основанный на файлах, чтобы понять как это все работает. Функции будут очень простые:
  1. // Определить папку для хранения кэшированных данных
  2. define ('CACHE_DIR',dirname(__FILE__).'/cache');
  3.  
  4. // Запись данных в кэш
  5. function save_cache($file$data) {
  6.     if ($f=fopen(CACHE_DIR.'/'.$file,'w')) {
  7.         fwrite($f,serialize($data));
  8.         fclose($f);
  9.         return true;
  10.     }
  11.     else {
  12.         return false;
  13.     }
  14. }
С записью проблем нет, мы просто создаем файл с заданным именем и записываем в него сериализованные данные. Чтение из кэша немного сложнее, так как сперва надо будет проверять наличие файла и, если требуется, его актуальность.
  1. // Чтение данных из кэша
  2. function load_cache($file$expire_time=0) {
  3.     $fname=CACHE_DIR.'/'.$file;
  4.     // Проверка наличия файла
  5.     if (file_exists($fname)) {
  6.         if ($expire_time) {
  7.             // Проверка актуальности файла
  8.             if ((time()-filemtime($fname))>=$expire_time) {
  9.                 return false;
  10.             }
  11.         }
  12.         // Прочитать данные
  13.         if ($f=fopen($fname,'r')) {
  14.             $tmp=fread($f,filesize($fname));
  15.             fclose($f);
  16.             return (unserialize($tmp));
  17.         }
  18.         else {
  19.             return false;
  20.         }
  21.     }
  22.     else {
  23.         return false;
  24.     }
  25. }
Как вариант, можно дополнительно сжимать данные перед записью в файл и распаковывать их после загрузки. Это допустимо в случаях, когда скорость чтения сжатых данных и последующей распаковки в памяти даст выигрыш перед чтением неупакованных данных с диска.

Функции для работы с кэшем у нас есть, можно применить их на конкретных примерах. Первый пример - чтение из базы данных. Время "жизни кэша" в этом примере равно 10 минутам (время передается в секундах).
  1. // Запрос к базе данных
  2. $query="SELECT `fucking_huge_data` FROM `big_slow_table`";
  3.  
  4. // Имя файла с закэшированными данными
  5. $file=md5($query);
  6.  
  7. // Попробовать загрузить данные из кэша, актуальность = 10 минут
  8. $res=load_cache($file10*60);
  9.  
  10. // Данные не загрузились или устарели, читаем их из базы
  11. if ($res===false) {
  12.     $sql_result=mysql_query($query);
  13.     $res=array();
  14.     while ($tmp=mysql_fetch_array($sql_result)) {
  15.         $res[]=$tmp;
  16.     }
  17.     // Сохранить данные в кэш
  18.     save_cache($file,$res);
  19. }
  20. // Операции с прочитанными или загруженными данными
  21. ...
Следующий пример - превращение динамической страницы в некое подобие статической. Для этого воспользуемся функций ob_start, чтобы получить содержимое страницы до отправки ее в браузер. О том, как работает эта функция, прочитайте в документации.
  1. // Имя файла для хранения закэшированной страницы
  2. $file=md5(getenv('REQUEST_URI'));
  3.  
  4. // Загрузить страницу из кэша
  5. $content=load_cache($file);
  6. if ($content) {
  7.     echo $content;
  8.     exit;
  9. }
  10.  
  11. // Начать буферизацию
  12. ob_start();
  13.  
  14. echo "<html>";
  15. echo "<head>";
  16. echo "  <title>Кэшированная страница</title>";
  17. echo "</head>";
  18. echo "<body>";
  19. echo "Эти данные закэшированы ".date('d.m.Y H:i.s')."<br>";
  20. echo "переданные параметры:";
  21. echo "<pre>";
  22. echo print_r($_GET,true)."</pre>";
  23. echo "</body>";
  24. echo "</html>";
  25.  
  26. // Получить страницу из буфера
  27. $content=ob_get_contents();
  28. // Сохранить данные в кэш
  29. save_cache($file,$content);
  30.  
  31. // Отправить страницу в браузер и очистить буфер
  32. ob_end_flush();
Имя файла кэша зависит от имени запрошенного скрипта и переданных ему методом GET параметров. Если такая страница уже запрашивалась ранее, то она должна быть в кэше на постоянном хранении. Иначе она динамически создается и записывается в кэш. Естественно, такой метод кэширования не допускается, если странице передаются параметры методом POST, но даже в этом случае все равно можно кэшировать неизменные фрагменты страницы.

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

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

Для проектов с большой нагрузкой лучше обратить внимание на nginx и Memcached, но это совершенно другая история. А для небольших сайтов вполне сгодится описанный в статье метод.

Поделиться ссылкой ВКонтакте
Просмотров: 6424 | Комментариев: 11

Метки: PHP
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
fag (03.07.2013 в 20:57):
Получилось исправить, чтобы выводило нормально.
// Данные не загрузились или устарели, читаем их из базы
if ($res===false) {
    $sql_result=mysql_query($query);
    $res=array();
    while ($tmp=mysql_fetch_array($sql_result)) {
        $res[]=$tmp;
    }
    // Сохранить данные в кэш

В строке $res[]=$tmp; убираем [], и тогда все нормально выводится
fag (03.07.2013 в 20:46):
Не получается через echo. Через var_dump выводит вот такое array(1) { [0]=> array(64) { [0]=> string(3) "498" ["id"]=> string(3) "498" [1]=> string(1) "2" и так далее... через echo $res[0] выводит Array
ManHunter (03.07.2013 в 20:19):
Можно так http://www.php.net/manual/ru/f...ion.echo.php Все зависит от данных.
fag (03.07.2013 в 19:42):
// Операции с прочитанными или загруженными данными
...

И вот как вывести эти данные?
saddam (14.01.2013 в 01:51):
Можете ли вы помочь в работе системного кэша на страницу моего сайта
ManHunter (19.03.2012 в 21:02):
Я пишу только о том, что использую сам и о том, что мне интересно. memcached у меня никаких эмоций не вызывает.
Дмитрий (19.03.2012 в 20:57):
ManHunter, по запросу "Кэширование на PHP" аналогичная картина, но вы же о нём написали
ManHunter (19.03.2012 в 01:08):
Поисковики по запросу "memcached" выдают кучу информации на любых языках и для любого уровня знаний.
Дмитрий (19.03.2012 в 00:15):
Было бы интересно почитать о работе с memcached
Станислав (01.03.2012 в 20:28):
Большое спасибо за обзор, помница я наверное даже просил про это в чате) сейчас зашёл и вот оно, главное написано понятным языком, вам бы в школе преподавать)))
morgot (01.03.2012 в 17:53):
Спасибо за интересную статью. В который раз уже нахожу у вас в блоге то, что нет нигде, и очень актуально.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.1 сек. / MySQL: 2 (0.0053 сек.) / Память: 4.5 Mb
Наверх