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 次后。
此外,删除 ftruncate
和 rewind
。
来自 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 中编写相应的例程可能更好(有人可能会争辩说 find
和 rm
是机器代码,因此比代码更快用 PHP 编写来完成相同的任务,但是对于所有 IO 重定向肯定会有 很多 的开销。
请做所有这些,然后看看它的表现有多糟糕。
如果结果仍然不能接受,我建议你写一些代码来测量这些功能的某些部分需要的时间(提示:microtime(true)
) or use a profiler, like XDebug,看看你的大部分时间到底花在哪里。
此外,您为什么关闭该块的错误报告?我觉得很可疑。
还有一点好处,您可以删除 $cc
,因为您不会在任何地方使用它。
我创建了一个用于我的网站的脚本,该脚本应该在需要缓存新项目时擦除缓存中最旧的条目。我的网站非常大,上面有 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 次后。
此外,删除 ftruncate
和 rewind
。
来自 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 中编写相应的例程可能更好(有人可能会争辩说 find
和 rm
是机器代码,因此比代码更快用 PHP 编写来完成相同的任务,但是对于所有 IO 重定向肯定会有 很多 的开销。
请做所有这些,然后看看它的表现有多糟糕。
如果结果仍然不能接受,我建议你写一些代码来测量这些功能的某些部分需要的时间(提示:microtime(true)
) or use a profiler, like XDebug,看看你的大部分时间到底花在哪里。
此外,您为什么关闭该块的错误报告?我觉得很可疑。
还有一点好处,您可以删除 $cc
,因为您不会在任何地方使用它。