【问题标题】:Linux/PHP File access issues (Building custom cache)Linux/PHP 文件访问问题(构建自定义缓存)
【发布时间】:2014-02-18 04:39:47
【问题描述】:

我有一个小问题,希望有人能解释一下情况。

情况:

我有一个内置在 PHP CMS 中的自定义部分缓存机制。简而言之,当处理 CMS 中的模板时,它会处理“可缓存”的 PHP 代码,而不处理“不可缓存”的 PHP 代码,然后将生成的代码保存为文件以供将来访问该文件进行处理页面。

问题:

当系统“寻找”缓存文件时,我遇到文件访问延迟。使用 GLOB 查找匹配文件的 250,000 个缓存文件在站点上没有流量时需要 1/4 秒 - 有时在流量高峰时需要 10-15 秒。似乎 2 个单独的客户端会话无法同时运行 GLOB,因此它们成为瓶颈。

我在寻找什么:

... 是一种替代方法,或提供块缓存的优化,没有瓶颈问题。必须考虑我的独特担忧(如下所述)。我需要一种更快的方法来访问这些文件或替代的部分页面缓存方向:/

===============================================

简化代码:

// var to hold cached page path, if found
$pageCache = NULL;

// get the URL for current page
// there is actually some other code here that could alter the 'theURL4Cache' var for various reasons, but for simplicity in this example lets just keep it the REQUEST_URI
$GLOBALS['theURL4Cache'] = $_SERVER['REQUEST_URI'];

// check if existing cache file is in place
$filePattern = 'parsed/page_cache/*^' . $_SERVER['SERVER_PORT'] . '^' . $_SERVER['HTTP_HOST'] . '^' . (($_SESSION['isMobile']) ? 'M' : 'D') . '^L' . $language . '^T*^P' . $attributes['pageId'] . '^' . md5($GLOBALS['theURL4Cache']) . sha1($GLOBALS['theURL4Cache']) . '.php';
$fileArr = glob($filePattern);

// possible multiple files found that fit / expired files found that fit the pattern
// lets grab the newest file and try to use it
if(count($fileArr)){ 
        rsort($fileArr);
        $file = $fileArr[0]; // get file with latest expire date
        if($file > 'parsed/page_cache/' . date('Y-m-d-H-i-s')) $pageCache = $file; // set an attribute to hold the valid file path to the cached file
        // remove files that are no longer corrent
        for($i=(($pageCache === NULL) ? 0 : 1); $i<count($fileArr);$i++) unlink($fileArr[$i]);
    };
if($pageCache){
  // cached page is found, lets process and output this puppy
  include($pageCache);
} else {
  // cached page is not found, let's build a cacheable page from the CMS template
  $newCode = // ..... various code is processed here to isolate the cacheable code blocks and process while leaving the non-cacheable blocks intact ..... //
  // create the new file path where the cached code will be placed
  // first we need an expiration date
  $cacheDate = date_create();
  date_add($cacheDate, date_interval_create_from_date_string( $cache_increment . ' ' . $cache_interval)); // $cache_increment and $cache_interval are stored in the CMS DB for each page, giving the content manager control over the expiration of the page in cache
  $filePath = $GLOBALS['iProducts']['physicalRoot'] . "/parsed/page_cache/" . date_format($cacheDate,'Y-m-d-H-i-s') . "^" . $_SERVER['SERVER_PORT'] . '^' . $_SERVER['HTTP_HOST'] . '^' . (($_SESSION['isMobile']) ? 'M' : 'D') ."^L" . $language . "^T" . $templateId . "^P" . $attributes['pageId'] . "^" .md5($GLOBALS['theURL4Cache']) . sha1($GLOBALS['theURL4Cache']) . '.php'; // create the file path
  if(file_exists($filePath)) unlink($filePath); // delete the file if it already exists
  $fp = fopen($filePath,"w"); // create the new file
  flock($fp,LOCK_EX);
  fwrite($fp,$newCode); // write the cache file
  flock($fp,LOCK_UN);
  // now output this puppy
  eval($newCode);
};

你问为什么你的文件名这么乱?

嗯,很高兴你问到! CMS 的另一部分包括“智能缓存管理”,如果页面或模板被内容管理器修改,所有受影响的缓存页面都会从系统中清除。此外,由于 URL 查询字符串中的属性,页面中的内容可能会有所不同,如果它是移动设备呈现与否,SSL 与非 SSL,域名或当前会话语言(引擎支持多种语言内容)关联到同一页面,根据会话语言有条件地输出。

所以这里是一个缓存页面文件名示例: 2014-02-14-10-36-36^80^www.mydomain.com^M^L^T42^P41^a067036ef358f12a0049740f035a7ee688dbb0033c19a70163d6c453dbc5b84f1889ffe2.php

以下是文件名的组成部分: expire-date^port^domain^mobileOrDesktop^Language^Template^Page^md5+sha1OfURL.php

以下是组件说明:

  • 过期日期:根据 CMS 中的内容管理器条目计算出此缓存文件应过期的日期/时间。 GLOB 可以使用它来过滤掉所有过期的文件并删除它们以通过 CRON 作业进行清理。这也是为了决定缓存页面是否足够新鲜,可以显示在代码的开头。
  • 端口:80 或 443 表示是否通过 SSL 检索。根据 SSL 状态,内容可能会有所不同。
  • domain: "www.mywebsite.com" 多个域名可以附加到一个 CMS 安装中,需要区分以便具有相同 REQUEST_URI 的两个域不会显示彼此的内容
  • 移动或桌面:“M”或“D” - 允许相同的网址“嗅出”客户端并相应地提供内容。
  • 语言:如果不使用多种语言,则为“L”,如果使用多种语言,则为“L-ENG / L-GER /...”
  • 模板:“T#”,因此 templateId 47 将是“T47” - 允许通过文件名轻松过滤以识别所有使用给定模板的缓存页面,以便在 CMS 中修改模板时删除。
  • page: "P#" 所以 pageId 12 将是 "P12" - 允许按文件名轻松过滤,以识别给定页面的所有缓存版本,以便在 CMS 中修改页面时删除。
  • md5+sha1OfURL.php:采用 $_SERVER['REQUEST_URI'] 并对其进行两次编码(一次 MD5,一次 SHA1)连接结果以给出一个(合理的)表示 URL 的唯一 ID(因为查询字符串会影响内容) .

欢迎任何想法或建议。提前致谢!

【问题讨论】:

  • 单个目录中有 250k 个文件?太疯狂了。每次运行 glob 操作时,您都在强制 Linux 加载目录文件并解析所有 250k 条目。您应该通过自动拆分到子目录来优化,例如如果您的文件哈希是abcdefg,您应该将文件放入a/b/c/abcdefg 类型的子目录模式。做任何最有意义的分层,并将每个目录的文件数保持在某个管理数。
  • 谢谢,Marc B。我曾考虑过自动拆分为子目录,但后来我遇到了其他问题,例如基于 CRON 清理的缓存到期日期进行搜索,以及基于其他因素进行搜索像 pageId 和 templateId (用于在 CMS 中更新页面/模板时“智能”清理缓存文件)。基本上它从彼得那里偷来给保罗。
  • find可以根据inode数据扫描文件,例如上次访问时间或上次修改时间。简而言之,就是您的扫描删除。
  • 您可能会考虑将这些文件符号链接到 cron 将查看的单独目录中?
  • Mark B:按访问时间/最后修改时间过滤会很好,因为所有页面都有相同的过期时间,但不幸的是它们没有,它们可以按页面或模板单独设置。

标签: php rhel5


【解决方案1】:

一些想法:

  1. 您可能需要考虑内存缓存系统,例如 memcached 或 redis,而不是磁盘缓存系统。读写磁盘的 io 负载可能很大。

  2. 有模板引擎(例如 SMARTY http://www.smarty.net/)可以为您完成很多繁重的工作。

  3. 您可能应该使用一致的散列规则来保存文件名,然后为您的标签使用一组辅助散列,而不是使用 glob 来搜索目录。例如,像这样保存所有文件:

    $fname = sha1($GLOBALS['theURL4Cache'].ALL_THE_REST_OF_THOSE_VARIABLES);
    $cachedir1 = substr($fname ,0,1);
    $cachedir2 = substr($fname,0,2);
    $finalname = 'parsed/page_cache/' . $cachedir1 . '/' .$cachedir2 .'/'. $fname . '.php';
    

这意味着一个 sha1 为 abcdefgh 的文件将被保存为“parsed/page_cache/a/ab/abcdefgh.php”。这将有助于减少文件系统退化。

现在,只要您知道文件名,就可以找到该文件 - 本质上,您将目录路径用作一个大哈希表。对于您的标签,设置另一组散列 - 这些可以作为文件保存在磁盘、数据库或内存中。每当将页面添加到您的标签之一时,请将 sha1 摘要或文件名添加到适当的哈希中。例如,在一个名为 'parseHashes/is_mobile.php' 的文件中,您可以有一个类似

的数组
$arr = array(
    "abcd",
    "defg"
   );

每个键都是您需要查找和/或删除的 sha1 摘要。当您需要操作标签中的文件集时,您只需遍历存储的 sha 摘要,您就会知道在哪里可以找到文件内容。

话虽如此,您确实应该为此研究基于内存甚至数据库的解决方案。在文件系统上执行此操作需要大量工作并且无法很好地扩展。

【讨论】:

  • 好主意!该系统最初是一个基于数据库(mysql)的缓存系统,但我遇到了行/表锁定问题——在测试了当前基于文件的方法后,我发现它更快更稳定。 memcache 解决方案是否能够管理该数据量(250,000 页?)。我将玩弄你推荐的一些组成部分,看看我是否可以让它满足其余的要求。
  • 你可能真的想看看 Redis 或 Memcached。我认为 Redis 内置了标记,因此它可能非常适合您的用例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多