AlexBB
Новичок
Организация самостоятельного кэширования для высоконагруженных проектов
Это не совсем вопрос. Хочется поделится своиими изысканиями в области разработки
проектов с информационным контентом на php, испытывающих сильные пиковые нагрузки.
Возможено кому-то они окажутся полезными. В тоже время, если уважанмое сообщество укажет
на недостатки предлагаемого подхода или предложит усовершенствования буду безмерно благодарен.
Итак, имеем проект реализованный на стандартной связке PHP+MySQL.
Время от времени (конкретно в моем случае раз в полчаса), контент-менеджер выполняет
обновления данных. Т.е. запускается php скрипт, выполнющий большой пакет SQL запросов
INSERT и/или UPDATE по большей части таблиц БД. Назовем его условно insert.php
Сами страницы сайта являются, в подавляющем большинстве, динамическими и для их отображения
требуется выполнения большого числа запросов SELECT, зачастую достоточно сложных, включающих
сложные условия, JOIN из нескольких таблиц и.т.д.
Практика и тестирование показали, что в системах подобной конфигурациях,
в моменты пиковой нагрузки, как правило узким местом оказывется база данных.
Таким образом, наша задача организовать кэширование страниц, чтоб ресурсоемкие
запросы SELECT выполнялись не более одного раза для каждой страницы.
Я решаю эту задачу следующим образом. Код несколько упрощен, так чтобы была понятна суть.
1. В БД создается таблица Сache c одним единственным полем Page типа Varchar
2. В скрипт insert.php добавляется следующее
3. В скрипты отображения страниц обавляется следующее
Буду, очень благодарен за любую критику и дополнения.
Это не совсем вопрос. Хочется поделится своиими изысканиями в области разработки
проектов с информационным контентом на php, испытывающих сильные пиковые нагрузки.
Возможено кому-то они окажутся полезными. В тоже время, если уважанмое сообщество укажет
на недостатки предлагаемого подхода или предложит усовершенствования буду безмерно благодарен.
Итак, имеем проект реализованный на стандартной связке PHP+MySQL.
Время от времени (конкретно в моем случае раз в полчаса), контент-менеджер выполняет
обновления данных. Т.е. запускается php скрипт, выполнющий большой пакет SQL запросов
INSERT и/или UPDATE по большей части таблиц БД. Назовем его условно insert.php
Сами страницы сайта являются, в подавляющем большинстве, динамическими и для их отображения
требуется выполнения большого числа запросов SELECT, зачастую достоточно сложных, включающих
сложные условия, JOIN из нескольких таблиц и.т.д.
Практика и тестирование показали, что в системах подобной конфигурациях,
в моменты пиковой нагрузки, как правило узким местом оказывется база данных.
Таким образом, наша задача организовать кэширование страниц, чтоб ресурсоемкие
запросы SELECT выполнялись не более одного раза для каждой страницы.
Я решаю эту задачу следующим образом. Код несколько упрощен, так чтобы была понятна суть.
1. В БД создается таблица Сache c одним единственным полем Page типа Varchar
2. В скрипт insert.php добавляется следующее
PHP:
$shm_id = shmop_open(SHMOP_LASTUPDATE, "c", 0644, 10); // Резирвируется память с адресом SHMOP_LASTUPDATE для сохранения времени обновления
shmop_write($shm_id, time(), 0); // В память записывается метка текущего времени
mysql_query('TRUNCATE TABLE Cache'); // Очищается Таблица Cache
PHP:
function CheckCache($CACHE) // Функция проверяет надо ли запускать динамическую генерацию страницы или взять ее из кэша. Входной параметр - имя траницы в дисковом кэше.
{
$res = mysql_query("SELECT COUNT(*) FROM Cache WHERE Page='$CACHE'"); // Проверяем есть ли наша страница уже в кэше
$wasprocessed = mysql_result($resultsetId, 1); // Если есть то $wasprocessed>0
if ($wasprocessed)
{
return false; // Свежая страница есть в кэше.
}
else // Если страницы нет в таблице Page надо ее генерить полюбому.
{
$file_time = @filemtime($CACHE); // Читаем время последнего изменения файла в кэше. На случай если он пропал, подавляем ошибку.
$httpheaders = getallheaders(); // Читаем http заголовки
$current_time = time(); // Берем текущую метку времени
$shm_id = shmop_open(SHMOP_LASTUPDATE, "c", 0644, 10); // Открываем память с адресом SHMOP_LASTUPDATE, где хранится метка времени последнего обновления
$lastupdate = shmop_read($shm_id, 0, 10); // Читаем метку времени последнего обновления
}
if (
!isset($file_time) || // Если вообще нет файла в кэше
($lastupdate - $file_time > OLDCHACHEFILE_SECONDS) || // Или если файл старее чем OLDCHACHEFILE_SECONDS секунд. OLDCHACHEFILE_SECONDS - определяем в зависимости от актуальности свежести данной страницы
(isset($httpheaders['Cache-Control']) && $httpheaders['Cache-Control'] == 'no-cache' && $file_time < $lastupdate) // Или если пользователь принудительно просит страницу не "из кэша" Ctrl+F5 и файл в кэше устарел. Это условие можно и убрать или добавить здесь проверку ip "своих пользователей".
)
return true; // Генерим страницу т.к. или ее нет или она слишком старая
else
return false; // Свежая или не слишком старая страница есть в кэше.
}
$CACHE = 'cache/index.html'; // Имя страницы, как она будет называться находясь в кэше. Сюда, в принципе, можно вставить автоматическую генерацию имен по URL
if (CheckCache($CACHE)) // Проверяем надо ли запускать динамическую генерацию страницы или взять ее из кэша
{
ob_start(); // Включаем буферицацию
//
// Здесь все ресурсоемкие запросы SELECT
//
$result = ob_get_contents(); // В переменную $result cбрасываем все результаты работы скрипта
ob_end_clean(); // Очищаем буфер
$res = file_put_contents($CACHE, $result); // Сохраняем резутьтат на диск в кэш
if ($res) // Если сохранение удачно, записываем в базу данных, что данная страница есть в кэше
{
mysql_query("INSERT INTO Саche(PAGE) VALUES('$CACHE')");
}
}
else // Сразу берем страницу из кэша. Сюда и должно попасть большая часть посетителей.
{
$result = file_get_contents($CACHE);
}
echo $result; // Выводим страницу