PHP原生mkdir()在高并发下因缺乏原子性导致竞态条件,多个请求同时判断并创建同目录会报“File exists”错误;应先is_dir()检查再mkdir(),并二次确认,或使用Symfony Filesystem组件自动防护。

PHP 中 mkdir() 在高并发场景下重复创建目录报错,核心问题是它不具备原子性——多个请求同时判断目录不存在、又几乎同时执行创建,就会触发竞态条件,导致部分请求失败(如 Warning: mkdir(): File exists 或返回 false)。
为什么原生 mkdir 会出问题
原生 mkdir($path, $mode, true) 不做存在性重检:它先尝试创建,若系统已存在该目录,就直接报错或返回 false,不会“安静跳过”。在 Web 请求密集或 CLI 多进程并行时(比如批量上传、日志归档、定时任务),这种时序冲突非常常见。
- 两个请求几乎同时执行
!is_dir($path)→ 都为true - 接着都调用
mkdir($path, 0755, true)→ 第二个必然失败 - 错误不拦截会导致后续逻辑中断(如文件写入失败、异常抛出)
推荐做法:先检查再创建,且容忍“已存在”
不要依赖 mkdir() 自动处理竞争,而是主动控制流程,把“目录已存在”当作正常路径而非错误:
- 用
is_dir($path)判断是否已存在,存在则直接跳过 - 不存在时调用
mkdir($path, 0755, true) - 无论成功与否,再补一次
is_dir($path)确认结果
示例代码:
立即学习“PHP免费学习笔记(深入)”;
$path = '/var/www/storage/logs/2026/11/02';if (!is_dir($path)) {
if (!mkdir($path, 0755, true) && !is_dir($path)) {
throw new RuntimeException("无法确保目录 {$path} 可用");
}
}
?>
更稳妥方案:用 Symfony Filesystem 组件
它内部封装了竞态防护逻辑,调用 $fs->mkdir($path) 时会自动处理“已存在”情况,静默通过,不抛错也不报 warning,还支持批量路径、符号链接、权限统一设置等:
- 安装:
composer require symfony/filesystem - 使用:$fs = new \Symfony\Component\Filesystem\Filesystem();
$fs->mkdir('/a/b/c'); // 安全,可并发 - 优势:避免手动写竞态防护逻辑,减少出错可能,尤其适合框架项目或长期维护系统
其他注意事项
即使加了防护,也要注意底层环境限制:
- NFS 或某些云存储挂载点对元操作延迟高,竞态概率更大,建议本地磁盘优先
- Linux 下确保父目录有
x权限(否则is_dir()也会失败) - 容器环境(如 Docker)中,确认运行用户(如
www-data)对目标路径有写权限和执行权限 - 避免在
open_basedir限制外操作,否则is_dir()和mkdir()都会静默失败



















