PHP中如何优化多次filesize()调用针对远程文件系统的执行性能?
PHP远程SMB文件系统filesize调用慢优化方案
核心优化逻辑
原代码性能瓶颈是单次filesize()调用对应一次SMB网络往返,串行执行下总耗时随文件数量线性增长,优化核心是减少网络请求次数或将串行请求改为并行执行。
无需额外扩展方案(优先推荐)
- 方案1:批量拉取目录元数据(同目录下文件最优)
利用FilesystemIterator遍历目录时会一次性拉取整个目录下所有文件的元数据,仅产生1次SMB网络往返,远低于逐个调用filesize()的N次开销。
代码示例:$targetDir = '/dir/'; $minSize = 100; // 跳过.和..目录,批量拉取元数据 $iterator = new FilesystemIterator($targetDir, FilesystemIterator::SKIP_DOTS); foreach ($iterator as $fileInfo) { $path = $fileInfo->getPathname(); if ($fileInfo->getSize() > $minSize) { echo "{$path} is larger than {$minSize} bytes\n"; } } // 若只需判断指定路径而非整个目录,可先拉取全目录大小缓存再匹配 $sizeCache = []; foreach ($iterator as $fileInfo) { $sizeCache[$fileInfo->getPathname()] = $fileInfo->getSize(); } foreach ($paths as $path => $_) { if (isset($sizeCache[$path]) && $sizeCache[$path] > $minSize) { echo "{$path} is larger than {$minSize} bytes\n"; } } - 方案2:系统命令批量查询(跨目录文件适用)
调用系统ls命令一次查询所有目标文件的大小,仅产生1次网络往返,适合文件分散在多个目录的场景。
代码示例:$minSize = 100; $pathArgs = implode(' ', array_map('escapeshellarg', array_keys($paths))); exec("ls -l {$pathArgs} | awk '{print \$9, \$5}'", $output); foreach ($output as $line) { $line = trim($line); if (empty($line)) continue; list($path, $size) = explode(' ', $line, 2); // 路径格式对齐处理 $path = '/' . ltrim($path, '/'); if ($size > $minSize) { echo "{$path} is larger than {$minSize} bytes\n"; } } - 方案3:本地缓存复用
如果目标文件大小更新频率低,可将查询到的文件大小缓存到本地(如APCu、临时文件、数组),缓存有效期内不再重复请求SMB,适合多次重复查询的场景。
扩展方案(性能更高)
- 方案1:smbclient扩展
直接调用libsmbclient的原生API批量获取文件元数据,比系统挂载SMB的方式开销更低,支持权限控制、批量操作等能力。
代码示例:// 需先安装smbclient扩展 $smb = new SmbClient('smb://服务器地址/共享目录/', '用户名', '密码'); $fileList = $smb->listFiles('/dir/'); $minSize = 100; foreach ($fileList as $file) { if (!$file['is_dir'] && $file['size'] > $minSize) { $path = "/dir/{$file['name']}"; echo "{$path} is larger than {$minSize} bytes\n"; } } - 方案2:Swoole协程并行请求
利用Swoole协程的异步IO能力,并行发起多个filesize()请求,总耗时等于单次请求的耗时,而非所有请求的耗时总和。
代码示例:// 需先安装swoole扩展 Co\run(function () use ($paths) { $minSize = 100; foreach ($paths as $path => $_) { Co::create(function () use ($path, $minSize) { if (filesize($path) > $minSize) { echo "{$path} is larger than {$minSize} bytes\n"; } }); } });
内容的提问来源于stack exchange,提问作者Georg Schölly
相关产品推荐
相关产品推荐

