最快的方式来比较目录状态,或哈希为乐趣和利润

我们有一个PHP应用程序,并且认为让应用程序知道自从上次执行以来是否发生了更改。 主要是由于pipe理caching等,并且知道我们的应用程序有时被不记得清除caching的人访问。 (改变人是显而易见的答案,但唉,不是真的可以实现的)

我们已经提出了这个问题,这是我们设法实现的最快速度,对于一个典型的项目,开发人员平均运行0.08。 我们已经尝试了shasum,md5和crc32,这是最快的。 我们基本上是每个文件的内容,然后输出。 安全并不是一个问题,我们只是希望通过不同的校验和来检测文件系统的变化。

time (find application/ -path '*/.svn' -prune -o -type f -print0 | xargs -0 md5 | md5) 

我想问题是,这可以进一步优化吗?

(我意识到修剪svn将会花费一些代价,但是查找只花费最less的时间,所以它会非常小,我们正在testing这个工作副本atm)

Solutions Collecting From Web of "最快的方式来比较目录状态,或哈希为乐趣和利润"

您可以使用inotify扩展来通知文件系统修改。

它可以安装pecl:

 pecl install inotify 

或者手动(像往常一样下载,phpize && ./configure && make && make install)。

这是一个Linux inotify系统调用的原始绑定,可能是linux上最快的解决方案。

看到这个简单的tail实现的例子: http : //svn.php.net/viewvc/pecl/inotify/trunk/tail.php?revision=262896&view=markup


如果你想要一个更高层次的库,或者对于非linux系统,请看看Lurker

它可以在任何系统上运行,并在可用时使用无效。

请参阅自述文件中的示例:

 $watcher = new ResourceWatcher; $watcher->track('an arbitrary id', '/path/to/views'); $watcher->addlistner('an arbitrary id', function (FilesystemEvent $event) { echo $event->getResource() . 'was' . $event->getTypeString(); }); $watcher->start(); 

可以使用与文件名和时间戳相同的技术来代替文件内容。

 find . -name '.svn' -prune -o -type f -printf '%m%c%p' | md5sum 

这比读取和散列每个文件的内容快得多。

我们不想使用FAM,因为我们需要将它安装在服务器上,而这并不总是可行的。 有时客户坚持我们部署在他们破碎的基础设施上。 由于它已经停产,所以很难得到这种改变也是明智的繁文</s>节。

在问题中提高原始版本速度的唯一方法是确保您的文件列表尽可能简洁。 IE浏览器只是散列的目录/文件,如果改变真的很重要。 删除不相关的目录可以大大提高速度。

过去,应用程序正在使用该函数来检查是否有更改以执行缓存清除(如果有)。 由于我们并不想在这样做的时候停止应用程序,所以这种事情最好是使用fsockopen作为一个异步过程。 这给总体上最好的“速度提升”,只是要注意比赛条件。

标记为“答案”,并提出FAM答案。

主动寻找变化,为什么不在事情发生变化时得到通知。 看看PHP的FAM – File Alteration Monitor API

FAM监视文件和目录,通知感兴趣的应用程序的变化。 有关FAM的更多信息,请访问» http://oss.sgi.com/projects/fam/ 。 一个PHP脚本可以指定一个文件列表供FAM使用这个扩展提供的功能进行监视。 当从任何应用程序到它的第一个连接打开时,FAM进程启动。 所有连接关闭后退出。

CAVEAT:在机器上需要一个额外的守护进程,并且PECL扩展没有维护。

既然你有svn,为什么不去修改。 我知道你正在跳过svn文件夹,但我想你做的速度优势,你没有修改文件在您的生产服务器…

那个说,你不必重新发明轮子。

您可以通过仅查看从目录索引(修改时间戳,文件大小等)读取的元数据来加速该过程,

你也可以停下来,一旦你发现了一个差异(理论上平均减少一半的时间)等有很多。

我真的认为在这种情况下最好的方法就是使用已经可用的工具。

Linux工具diff有一个-q选项(快速)。

您将需要使用递归参数-r

diff -r -q dir1/ dir2/

它使用了很多的优化,我严重怀疑你可以显着改善,而不费力气。

当然,你应该使用的是Inotify它的快速和易于配置,直接从bash或php的多个选项为这个任务奉献一个简单的节点inotify实例

但是Inotify不能在Windows上使用,但是可以使用FileSystemWatcher或FindFirstChangeNotification轻松编写命令行应用程序,并通过exec调用

如果您只是在寻找PHP solution那么它很难,你可能得不到性能要求,因为唯一的办法是连续扫描该文件夹

这是一个简单的实验

  • 不要在生产中使用
  • 无法管理大型文件集
  • 不支持文件监视
  • 只支持新的,删除和修改
  • 不支持递归

 if (php_sapi_name() !== 'cli') die("CLI ONLY"); date_default_timezone_set("America/Los_Angeles"); $sm = new Monitor(__DIR__ . "/test"); // Add hook when new files are created $sm->hook(function ($file) { // Send a mail or log to a file printf("#EMAIL NEW FILE %s\n", $file); }, Monitor::NOTIFY_NEW); // Add hook when files are Modified $sm->hook(function ($file) { // Do monthing meaningful like calling curl printf("#HTTP POST MODIFIED FILE %s\n", $file); }, Monitor::NOTIFY_MODIFIED); // Add hook when files are Deleted $sm->hook(function ($file) { // Crazy ... Send SMS fast or IVR the Boss that you messed up printf("#SMS DELETED FILE %s\n", $file); }, Monitor::NOTIFY_DELETED); // Start Monitor $sm->start(); 

使用缓存

 // Simpe Cache // Can be replaced with Memcache class Cache { public $f; function __construct() { $this->f = fopen("php://temp", "rw+"); } function get($k) { rewind($this->f); return json_decode(stream_get_contents($this->f), true); } function set($k, $data) { fseek($this->f, 0); fwrite($this->f, json_encode($data)); return $k; } function run() { } } 

实验班

 // The Experiment class Monitor { private $dir; private $info; private $timeout = 1; // sec private $timeoutStat = 60; // sec private $cache; private $current, $stable, $hook = array(); const NOTIFY_NEW = 1; const NOTIFY_MODIFIED = 2; const NOTIFY_DELETED = 4; const NOTIFY_ALL = 7; function __construct($dir) { $this->cache = new Cache(); $this->dir = $dir; $this->info = new SplFileInfo($this->dir); $this->scan(true); } public function start() { $i = 0; $this->stable = (array) $this->cache->get(md5($this->dir)); while(true) { // Clear System Cache at Intervals if ($i % $this->timeoutStat == 0) { clearstatcache(); } $this->scan(false); if ($this->stable != $this->current) { $this->cache->set(md5($this->dir), $this->current); $this->stable = $this->current; } sleep($this->timeout); $i ++; // printf("Memory Usage : %0.4f \n", memory_get_peak_usage(false) / // 1024); } } private function scan($new = false) { $rdi = new FilesystemIterator($this->dir, FilesystemIterator::SKIP_DOTS); $this->current = array(); foreach($rdi as $file) { // Skip files that are not redable if (! $file->isReadable()) return false; $path = addslashes($file->getRealPath()); $keyHash = md5($path); $fileHash = $file->isFile() ? md5_file($path) : "#"; $hash["t"] = $file->getMTime(); $hash["h"] = $fileHash; $hash["f"] = $path; $this->current[$keyHash] = json_encode($hash); } if ($new === false) { $this->process(); } } public function hook(Callable $call, $type = Monitor::NOTIFY_ALL) { $this->hook[$type][] = $call; } private function process() { if (isset($this->hook[self::NOTIFY_NEW])) { $diff = array_flip(array_diff(array_keys($this->current), array_keys($this->stable))); $this->notify(array_intersect_key($this->current, $diff), self::NOTIFY_NEW); unset($diff); } if (isset($this->hook[self::NOTIFY_DELETED])) { $deleted = array_flip(array_diff(array_keys($this->stable), array_keys($this->current))); $this->notify(array_intersect_key($this->stable, $deleted), self::NOTIFY_DELETED); } if (isset($this->hook[self::NOTIFY_MODIFIED])) { $this->notify(array_diff_assoc(array_intersect_key($this->stable, $this->current), array_intersect_key($this->current, $this->stable)), self::NOTIFY_MODIFIED); } } private function notify(array $files, $type) { if (empty($files)) return; foreach($this->hook as $t => $hooks) { if ($t & $type) { foreach($hooks as $hook) { foreach($files as $file) { $info = json_decode($file, true); $hook($info['f'], $type); } } } } } }