Apache/PHP 在尝试释放缓存时使用 100% CPU space

Apache/PHP using 100% CPU while trying to free cache space

我创建了一个用于我的网站的脚本,该脚本应该在需要缓存新项目时擦除缓存中最旧的条目。我的网站非常大,上面有 500,000 张照片,缓存 space 设置为 2 GB。

这些函数是造成问题的原因:

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() 是 returns 正确的文件夹名称并附加 / 的函数。

执行功能时,CPUapache 的使用率在 95% 到 100% 之间,服务器上的其他服务在此期间访问速度极慢。我还注意到 whm 缓存磁盘使用率为 100%,并且在我清除缓存之前拒绝下降。我期待的可能是 90%。

我试图用 cache_tofile 函数做的是尝试释放磁盘 space 以创建文件夹,然后释放磁盘 space 以创建缓存文件。 cache_space_make 函数采用一个参数表示要释放的磁盘数量 space。

在那个函数中,我使用系统调用来尝试在整个缓存的目录树中找到最旧的文件,但我无法找到本机 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 的源代码时看到的内容相同的内容)

缓存系统的目的是以最佳速度传送所有网页。

那么我的问题是如何防止系统在缓存已满时尝试锁定。有没有比我提供的代码更好的代码?

我首先将您代码中的 || 替换为 &&,这很可能是我的意图。
目前,循环将 always 运行 至少 1000 次 - 我非常希望本意是 停止尝试 1000 次后。

此外,删除 ftruncaterewind
来自 PHP Manual on fopen(强调我的):

'w'       Open for writing only; place the file pointer at the beginning of the file and truncate the
            file to zero length
. If the file does not exist, attempt to create it.

所以你的truncate是多余的,你的rewind也是多余的。

接下来,检查您的 shell_exec
循环外的那个对我来说似乎不是太大的瓶颈,但是内部循环...
假设您在该缓存文件夹中有 1'000'000 个文件。
find 将很乐意为您列出所有这些,无论需要多长时间。
然后你对该列表进行排序。
然后你把那个列表的 999'999 个条目冲进马桶,只保留第一个。
然后你用 awk 做了一些我不太关心的事情,然后你删除了文件。
在下一次迭代中,您只需浏览 999'999 个文件,其中您 仅丢弃 999'998.
看到我要去的地方了吗?
无论如何,我考虑调用 shell 脚本纯粹是出于方便的坏习惯,但如果你这样做,至少要尽可能高效!
在没有 head -1 的情况下执行一个 shell_exec,将结果列表存储在一个变量中,然后对其进行迭代。
尽管完全放弃 shell_exec 并在 PHP 中编写相应的例程可能更好(有人可能会争辩说 findrm 是机器代码,因此比代码更快用 PHP 编写来完成相同的任务,但是对于所有 IO 重定向肯定会有 很多 的开销。

请做所有这些,然后看看它的表现有多糟糕。
如果结果仍然不能接受,我建议你写一些代码来测量这些功能的某些部分需要的时间(提示:microtime(true)) or use a profiler, like XDebug,看看你的大部分时间到底花在哪里。

此外,您为什么关闭该块的错误报告?我觉得很可疑。

还有一点好处,您可以删除 $cc,因为您不会在任何地方使用它。