PHP文件锁用flock()实现互斥写入,需先fopen()再flock(LOCK_EX),所有IO必须经该文件指针,file_put_contents的LOCK_EX仅保证单次写入原子性,无法保护“读-改-写”多步操作。

PHP 文件锁怎么实现互斥写入
用 flock() 最直接,但必须注意它默认是阻塞的,且只对同一文件句柄有效——不是“全局锁”,而是“当前进程+当前打开的文件流”级别的协作式锁。
- 必须先用
fopen()打开文件(哪怕只是'r'模式),再调用flock(),不能对路径字符串直接加锁 - 加锁后所有读写操作都得通过该文件指针进行,
file_put_contents($path, $data)这类函数会绕过锁,导致失效 - 解锁靠
flock($fp, LOCK_UN)或脚本结束时自动释放(但别依赖自动释放,尤其在 long-running 进程中)
flock() 的 LOCK_EX 和 LOCK_SH 区别在哪
LOCK_EX 是排他锁(写锁),同一时间只允许一个进程持有;LOCK_SH 是共享锁(读锁),多个进程可同时持有,但只要有 LOCK_EX 在,LOCK_SH 就会阻塞。
- 想保证写入不被并发覆盖 → 用
LOCK_EX - 只是读取、又希望避免读到写到一半的脏数据 → 可配合
LOCK_SH,但需写方也主动用LOCK_EX,否则读锁没意义 - 加锁失败时
flock()返回false,不是抛异常,记得判断
为什么 file_put_contents(..., LOCK_EX) 不够用
file_put_contents($path, $data, LOCK_EX) 确实会内部调用 flock(),但它只锁住“这次写入”的瞬间:打开→加锁→写入→关闭。整个过程极短,无法覆盖“读-改-写”这类多步操作。
- 比如:读出 JSON → 解码 → 修改某个字段 → 编码 → 写回。这四步之间没有锁保护,其他进程可能在中间插入写入
- 这种场景必须手动用
fopen()+flock()+fwrite()+fclose()把整段逻辑包住 -
LOCK_EX标志位对file_put_contents()是“一次性原子写”,不是“会话级锁”
文件锁失效的常见坑
最典型的是 NFS 或某些容器挂载卷上 flock() 不生效——因为 POSIX 文件锁在部分网络文件系统上被禁用或模拟不完整。
立即学习“PHP免费学习笔记(深入)”;
- 测试方法:起两个终端,分别运行带
flock()的脚本,看是否真能互斥 - Linux 下可通过
strace -e trace=flock php script.php确认系统调用是否成功 - 如果环境不可靠,得降级用目录锁(
mkdir()原子性)或外部服务(Redis SETNX)











