Apache / PHP использует 100% CPU при попытке освободить место в кеше

Я создал сценарий для использования с моим веб-сайтом, который должен удалять самую старую запись в кэше, когда новый элемент необходимо кэшировать. Мой веб-сайт очень большой, на нем 500 000 фотографий, а объем кеша составляет 2 ГБ.

Эти функции являются причиной проблем:

function cache_tofile($fullf, $c)
{
error_reporting(0);
if(strpos($fullf, "/") === FALSE)
{
$fullf = "./".$fullf;
}
$lp = strrpos($fullf, "/");
$fp = substr($fullf, $lp + 1);
$dp = substr($fullf, 0, $lp);
$sz = strlen($c);
cache_space_make($sz);
mkdir($dp, 0755, true);
cache_space_make($sz);
if(!file_exists($fullf))
{
$h = @fopen($fullf, "w");
if(flock($h, LOCK_EX))
{
ftruncate($h, 0);
rewind($h);
$tmo = 1000;
$cc = 1;
$i = fputs($h, $c);
while($i < strlen($c) || $tmo-- > 1)
{
$c = substr($c, $i);
$i = fwrite($h, $c);
}
flock($h, LOCK_UN);
fclose($h);
}
}
error_reporting(7);
}

function cache_space_make($sz)
{
$ct = 0;
$cf = cachefolder();
clearstatcache();
$fi = shell_exec("df -i ".$cf." | tail -1 | awk -F\" \" '{print \$4}'");
if($fi < 1)
{
return;
}
if(($old = disk_free_space($cf)) === false)
{
return;
}
while($old < $sz)
{
$ct++;
if($ct > 10000)
{
error_log("Deleted over 10,000 files. Is disk screwed up?");
break;
}
$fi = shell_exec("rm \$(find ".$cf."cache -type f -printf '%T+ %p\n' | sort | head -1 | awk -F\" \" '{print \$2}');");
clearstatcache();
$old = disk_free_space($cf);
}
}

cachefolder() это функция, которая возвращает правильное имя папки с / добавлен к нему.

Когда функции выполняются, загрузка ЦП для apache составляет от 95% до 100%, и другие службы на сервере очень медленно получают доступ в течение этого времени. Я также заметил в WHM, что использование диска кеша на 100% и отказывается падать, пока я не очистить кеш. Я ожидал большего, примерно, как 90%.

То, что я пытаюсь сделать с функцией cache_tofile, это попытка освободить место на диске, чтобы создать папку, а затем освободить место на диске для создания файла кэша. Функция cache_space_make принимает один параметр, представляющий количество дискового пространства, которое нужно освободить.

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

Формат файла кэша следующий:

/cacherootfolder/requestedurl

Например, если один запрос http://www.example.com/abc/def затем из обеих функций папка, которая должна быть создана, называется abc, а затем файл определяется как def, поэтому весь файл в системе будет:

/cacherootfolder/abc/def

Если один просит http://www.example.com/111/222 тогда папка 111 будет создана и файл 222 будет создан

/cacherootfolder/111/222

Каждый файл в обоих случаях содержит тот же контент, что и пользователь, запрашиваемый на основе URL. (пример: / cacherootfolder / 111/222 содержит тот же контент, что и при просмотре источника из http://www.example.com/111/222)

Целью системы кэширования является доставка всех веб-страниц с оптимальной скоростью.

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

1

Решение

Я бы начал с замены || в вашем коде &&что было, скорее всего, намерением.
В настоящее время цикл будет всегда бежать не менее 1000 раз — Я очень надеюсь, что намерение было перестань пытаться после 1000 раз.

Кроме того, бросьте ftruncate а также rewind,
От Руководство по PHP на fopen (выделение мое):

‘w’ Открыто только для записи; поместите указатель файла в начале файла и обрезать
            файл до нулевой длины
. Если файл не существует, попытайтесь создать его.

Так что ваши truncate избыточен, как и ваш rewind,

Далее просмотрите ваш shell_exec«S.
То, что за пределами петли, не кажется мне слишком узким местом, но одно внутри петля…
Допустим, у вас есть 1 000 000 файлов в этой папке кэша.
find с радостью перечислю их всех для вас, независимо от того, сколько времени это займет.
Затем вы сортируете этот список.
А потом вы сбрасываете 999’999 записей этого списка в унитаз, и оставь только первый.
Затем вы делаете некоторые вещи с awk что мне действительно все равно, а потом вы удаляете файл.
На следующей итерации вам нужно будет только просмотреть 999’999 файлов, из которых вы сбросить только 999’998.
Видишь, куда я иду?
В любом случае, я считаю, что вызывать сценарии оболочки из чистого удобства — плохая практика, но если вы делаете это, делайте это максимально эффективно, по крайней мере!
Выполните одно shell_exec без head -1сохраните полученный список в переменной и выполните итерации по нему.
Хотя может быть лучше отказаться shell_exec в целом, и вместо этого запрограммировать соответствующие процедуры в PHP (можно утверждать, что find а также rm являются машинным кодом, и, следовательно, быстрее, чем код, написанный на PHP, чтобы выполнить ту же задачу, но есть много накладных расходов на все это перенаправление IO).

Пожалуйста, сделайте все это, а затем посмотрите, как плохо это все еще работает.
Если результаты по-прежнему неприемлемы, я предлагаю вам добавить код для измерения времени, которое требуется определенным частям этих функций (совет: microtime(true)) или использовать профилировщик, как XDebug, чтобы увидеть, где именно большая часть вашего времени проводится.

Кроме того, почему вы отключили отчеты об ошибках для этого блока? Выглядит более чем подозрительно для меня.

И как маленький бонус, вы можете избавиться от $cc так как вы нигде не используете его.

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector