PHP文件锁核心是flock()对文件句柄加独占锁,需严格按打开→加锁→写入→解锁→关闭执行;file_put_contents()内置LOCK_EX可简化操作;高并发或跨服务器场景应改用Redis队列等替代方案。

PHP文件锁机制的核心是用 flock() 对打开的文件句柄加独占锁,确保同一时间只有一个进程能写入目标文件。它不依赖外部服务,轻量且兼容性好,但必须严格按“打开→加锁→写入→解锁→关闭”顺序执行,跳过任一环节都可能失效。
正确使用flock()的关键操作
文件锁不是对路径起作用,而是作用于 fopen() 返回的资源句柄。常见错误是直接对字符串路径调用 flock(),结果永远返回 false。
- 打开文件推荐用
'c+'(不存在则创建、可读写、不截断)或'a+'(追加模式、可读),避免用纯'a'—— 它会跳过锁前的指针定位,导致写入位置错乱 - 加锁必须用非阻塞方式:
flock($fp, LOCK_EX | LOCK_NB),失败立即返回false,便于你控制重试或降级逻辑 - 写入完成后必须显式调用
flock($fp, LOCK_UN),不能依赖脚本结束或fclose()自动释放(PHP 7.3+ 已移除该行为) - 建议将加锁和解锁放入
try...finally块中,覆盖exit、未捕获异常等异常退出路径
file_put_contents 的简化方案
如果只是简单追加日志或配置内容,file_put_contents() 提供了内置锁支持,一行代码即可启用:
file_put_contents('/var/log/app.log', $msg . "\n", FILE_APPEND | LOCK_EX);
该方式底层仍调用 flock(),自动完成开/锁/写/解/关全流程,适合无复杂逻辑的场景。但注意:它不支持自定义超时或重试策略,锁失败时直接返回 false,需自行判断处理。
立即学习“PHP免费学习笔记(深入)”;
Web 环境下的典型陷阱
CLI 脚本里锁通常稳定,但 PHP-FPM 或 Apache 下容易出问题,根源在于进程模型和句柄生命周期:
- 每个 HTTP 请求是独立 worker 进程,
flock()本身是进程级的,没问题;但若启用了opcache.file_cache或 APCu 缓存了句柄变量,可能导致锁对象被意外复用 - Nginx + PHP-FPM 组合中,若使用
fastcgi_finish_request()提前返回响应,后续异步逻辑再操作同一锁文件,句柄可能已被回收 - 调试时可用
lsof -p $PID | grep lockfile查看当前进程是否还持有句柄,比凭空猜测更可靠
替代方案与适用边界
当单机写入量持续超过每秒 300–500 条,或需跨服务器聚合日志、做实时分析时,flock() 的串行化瓶颈会显现。此时应转向消息队列:
- Redis List 方案:业务端
LPUSH app:log:queue $msg,守护进程BRPOP app:log:queue 0持续消费并批量落盘 - 关键点:复用 Redis 连接实例,避免每次新建连接;设置合理的
BRPOP超时,防止空轮询耗 CPU - 优势在于解耦主流程,避免日志写入拖慢接口响应,也天然规避了文件锁争用问题



















