flock() 是唯一可靠、跨平台的文件锁定机制,因它通过内核级锁绑定文件句柄确保并发写入安全,而其他方法不跨进程或依赖外部服务;需在 fopen() 后立即加锁,锁随句柄关闭自动释放,仅适用于本地磁盘且须配合 fflush() 和正确文件模式。
flock() 是 PHP 中唯一可靠、跨平台的文件锁定机制,能防止并发写入导致的数据损坏。其他方法(如重命名、信号量)要么不跨进程,要么依赖外部服务,实际不可靠。
为什么 flock() 是唯一推荐方案
PHP 没有原子级文件写入,直接 fwrite() + fclose() 在多请求场景下极易丢数据。比如两个请求同时读取 JSON 文件、各自修改后写回,后写入者会覆盖前者的变更。flock() 通过操作系统内核级锁确保同一时间只有一个进程可操作该文件句柄。
- 必须在
fopen()之后、任何读写之前调用flock($fp, LOCK_EX) - 锁是与文件句柄绑定的,不是文件路径;关闭
$fp或脚本结束时自动释放 - 不支持 NFS 等网络文件系统(会静默失败),本地磁盘才可靠
- 阻塞式调用默认会挂起,可用
LOCK_EX | LOCK_NB改为非阻塞并手动处理失败
典型安全修改流程(含错误处理)
以下是最小可行的安全写入模式,覆盖打开、加锁、读取、修改、写入、刷新、解锁全流程:
$path = '/tmp/data.json';
$fp = fopen($path, 'c+'); // 'c+' 确保文件存在且可读写,不截断
if (!$fp) {
throw new RuntimeException("无法打开文件: $path");
}
if (!flock($fp, LOCK_EX)) {
fclose($fp);
throw new RuntimeException("无法获取排他锁: $path");
}
// 读取当前内容(注意:文件指针在开头)
rewind($fp);
$content = stream_get_contents($fp);
$data = json_decode($content, true) ?: [];
// 修改数据
$data['updated'] = time();
// 写入前清空并重置指针
fseek($fp, 0);
ftruncate($fp, 0);
fwrite($fp, json_encode($data, JSON_UNESCAPED_UNICODE));
fflush($fp); // 强制刷到磁盘,避免缓冲区延迟
flock($fp, LOCK_UN);
fclose($fp);
常见失效场景和避坑点
很多“锁了但还是出错”的问题,根源不在 flock() 本身,而在使用方式: