0

0

PHP并发数据写入:使用文件锁防止数据丢失的教程

聖光之護

聖光之護

发布时间:2025-10-18 10:36:01

|

1007人浏览过

|

来源于php中文网

原创

PHP并发数据写入:使用文件锁防止数据丢失的教程

本文探讨了在javascript频繁向php服务器传输数据时,因并发写入同一文件导致的竞态条件和数据丢失问题。通过引入php文件锁机制,确保数据写入的原子性,即在同一时间只有一个进程能修改文件,从而有效防止数据丢失,保障数据完整性。

理解并发写入与数据丢失的根源

在现代Web应用中,客户端(如JavaScript)向服务器频繁发送数据是常见操作。当多个客户端或单个客户端在短时间内连续向服务器发送数据,并且服务器端需要将这些数据写入同一个文件时,就可能出现数据丢失的问题。这通常是由“竞态条件”(Race Condition)引起的。

考虑以下场景:一个JavaScript客户端通过XHR请求向PHP后端发送数据。PHP脚本接收数据后,会读取一个JSON文件,将新数据追加进去,然后将更新后的数据写回文件。

JavaScript 客户端代码示例:

const XHR = new XMLHttpRequest();

function sendData(data) {
  XHR.open('POST', 'savedata.php');
  XHR.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  XHR.send('data=' + JSON.stringify(data));
}

// 假设在短时间内多次调用 sendData(someObject);
// 例如:
// sendData({ id: 1, value: 'test1' });
// sendData({ id: 2, value: 'test2' });

原始的 PHP 服务器端处理逻辑:

立即学习PHP免费学习笔记(深入)”;

if (isset($_POST['data'])) {
    if (file_exists('data.json')) {
        $file = file_get_contents('data.json'); // 进程A读取文件
        $accumulatedData = json_decode($file); // 进程A解码数据
        $data = json_decode($_POST['data']);
        array_push($accumulatedData, $data); // 进程A修改数据
        $encodedAccumulatedData = json_encode($accumulatedData);
        file_put_contents('data.json', $encodedAccumulatedData); // 进程A写入文件
    }
}

上述PHP代码存在一个严重的问题。如果两个或多个PHP进程几乎同时执行这段代码:

  1. 进程A 读取 data.json。
  2. 进程B 几乎同时读取 data.json(此时 data.json 的内容与进程A读取时相同)。
  3. 进程A 将新数据追加到其内存中的 $accumulatedData,并编码
  4. 进程B 也将新数据追加到其内存中的 $accumulatedData(基于旧的文件内容),并编码。
  5. 进程A 将其更新后的数据写入 data.json。
  6. 进程B 将其更新后的数据写入 data.json,覆盖了进程A写入的内容。

结果是,进程A提交的数据会丢失,因为进程B基于一个旧版本的文件内容进行了修改并最终覆盖了文件。

hkcms双语言响应式科技类通用模板1.0.0
hkcms双语言响应式科技类通用模板1.0.0

hkcms双语言响应式科技类通用模板是使用hkcms开源内容管理系统开发的一套响应式模板。内有新闻列表、新闻详情、单页、产品列表,产品详情页等。1.使用的框架采用HkCms开源内容管理系统v2.2.1版本2. 所需环境Apache/NginxPHP7.2 及以上 + MySQL 5.6 及以上3. 安装教程:1. 打开根目录,导入根目录下的数据库文件2. 站点运行路径填写到public目录下3.

下载

解决方案:使用文件锁(File Locking)

为了解决这种竞态条件导致的数据丢失问题,我们需要确保对共享资源的访问是原子性的,即在任何给定时间,只有一个进程能够修改文件。PHP提供了 flock() 函数来实现文件锁定。

flock() 函数允许你对一个打开的文件句柄进行读锁或写锁。当一个进程获得文件的独占写锁时,其他试图获取锁的进程将被阻塞,直到当前锁被释放。

使用 flock() 改进的 PHP 服务器端代码:

代码解析与注意事项

  1. fopen($filePath, "r+"): 以读写模式打开文件。r+ 模式允许你读取文件内容,也可以从文件开头写入(会覆盖)。如果文件不存在,fopen 会返回 false。我们增加了在文件不存在时先创建空JSON数组的逻辑。
  2. flock($fp, LOCK_EX): 这是核心。它尝试获取文件的独占锁。LOCK_EX 表示独占锁,即同一时间只有一个进程可以持有此锁。如果文件已被其他进程锁定,当前进程将在此处阻塞,直到锁被释放。
  3. stream_get_contents($fp, -1, 0): 在获取锁后,我们使用此函数从文件开头读取所有内容。这比 file_get_contents() 更安全,因为它操作的是已打开并锁定的文件句柄,避免了潜在的竞态条件。
  4. rewind($fp) 和 ftruncate($fp, 0): 在写入新数据之前,将文件指针重置到文件开头 (rewind()),然后使用 ftruncate($fp, 0) 截断文件,将其大小设置为0,清除所有旧内容。这是确保新数据完整写入的关键步骤。
  5. fwrite($fp, $encodedAccumulatedData): 将编码后的新数据写入文件。
  6. flock($fp, LOCK_UN): 完成文件操作后,务必释放锁。这允许其他等待的进程获取锁并继续执行。
  7. fclose($fp): 关闭文件句柄,释放系统资源。
  8. 错误处理: 增加了文件打开失败、JSON解析失败、客户端数据无效等情况的错误处理,提高了脚本的健壮性。使用 error_log 记录服务器端错误,并向客户端返回适当的HTTP状态码和消息。
  9. JSON解码为关联数组: json_decode($fileContent, true) 将JSON字符串解码为PHP关联数组,这通常比对象更容易操作。

替代方案与更高并发场景

虽然文件锁对于中低并发场景有效,但在极高并发环境下,频繁的文件锁定和释放可能会成为性能瓶颈。对于需要处理大量并发写入的场景,以下是更优的替代方案:

  1. 数据库: 使用关系型数据库(如MySQL, PostgreSQL)或NoSQL数据库(如MongoDB, Redis)是处理结构化数据的首选。数据库系统本身提供了强大的并发控制机制(事务、行锁等),能够高效、安全地处理并发写入。
  2. 消息队列(Message Queue): 对于写入操作可以异步处理的场景,可以将数据先发送到消息队列(如RabbitMQ, Kafka)。后端消费者进程从队列中取出数据,然后以受控的速率写入文件或数据库。这可以平滑峰值流量,提高系统的响应能力和吞吐量。
  3. 原子写入操作: 某些文件系统或库提供原子性的文件写入操作,即写入要么完全成功,要么完全失败,不会出现中间状态。但这通常需要更底层的系统支持。

总结

在服务器端处理并发数据写入时,防止数据丢失是确保数据完整性的关键。通过理解竞态条件,并利用PHP的 flock() 函数实现文件锁定,我们可以有效地避免在文件操作过程中出现数据覆盖和丢失的问题。对于更高级的并发需求,考虑采用数据库或消息队列等成熟的解决方案,以构建更健壮、可扩展的系统。始终记住,在处理共享资源时,原子性操作是保障数据一致性的基石。

相关专题

更多
php文件怎么打开
php文件怎么打开

打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

1970

2023.09.01

php怎么取出数组的前几个元素
php怎么取出数组的前几个元素

取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

1294

2023.10.11

php反序列化失败怎么办
php反序列化失败怎么办

php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

1199

2023.10.11

php怎么连接mssql数据库
php怎么连接mssql数据库

连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

948

2023.10.23

php连接mssql数据库的方法
php连接mssql数据库的方法

php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

1400

2023.10.23

html怎么上传
html怎么上传

html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

1229

2023.11.03

PHP出现乱码怎么解决
PHP出现乱码怎么解决

PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1439

2023.11.09

php文件怎么在手机上打开
php文件怎么在手机上打开

php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1303

2023.11.13

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MySQL 教程
MySQL 教程

共48课时 | 1.5万人学习

MySQL 初学入门(mosh老师)
MySQL 初学入门(mosh老师)

共3课时 | 0.3万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 778人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号