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() 函数实现文件锁定,我们可以有效地避免在文件操作过程中出现数据覆盖和丢失的问题。对于更高级的并发需求,考虑采用数据库或消息队列等成熟的解决方案,以构建更健壮、可扩展的系统。始终记住,在处理共享资源时,原子性操作是保障数据一致性的基石。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
mysql修改数据表名
mysql修改数据表名

MySQL修改数据表:1、首先查看数据库中所有的表,代码为:‘SHOW TABLES;’;2、修改表名,代码为:‘ALTER TABLE 旧表名 RENAME [TO] 新表名;’。php中文网还提供MySQL的相关下载、相关课程等内容,供大家免费下载使用。

668

2023.06.20

MySQL创建存储过程
MySQL创建存储过程

存储程序可以分为存储过程和函数,MySQL中创建存储过程和函数使用的语句分别为CREATE PROCEDURE和CREATE FUNCTION。使用CALL语句调用存储过程智能用输出变量返回值。函数可以从语句外调用(通过引用函数名),也能返回标量值。存储过程也可以调用其他存储过程。php中文网还提供MySQL创建存储过程的相关下载、相关课程等内容,供大家免费下载使用。

247

2023.06.21

mongodb和mysql的区别
mongodb和mysql的区别

mongodb和mysql的区别:1、数据模型;2、查询语言;3、扩展性和性能;4、可靠性。本专题为大家提供mongodb和mysql的区别的相关的文章、下载、课程内容,供大家免费下载体验。

281

2023.07.18

mysql密码忘了怎么查看
mysql密码忘了怎么查看

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql密码忘了怎么办呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

515

2023.07.19

mysql创建数据库
mysql创建数据库

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql怎么创建数据库呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

256

2023.07.25

mysql默认事务隔离级别
mysql默认事务隔离级别

MySQL是一种广泛使用的关系型数据库管理系统,它支持事务处理。事务是一组数据库操作,它们作为一个逻辑单元被一起执行。为了保证事务的一致性和隔离性,MySQL提供了不同的事务隔离级别。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

386

2023.08.08

sqlserver和mysql区别
sqlserver和mysql区别

SQL Server和MySQL是两种广泛使用的关系型数据库管理系统。它们具有相似的功能和用途,但在某些方面存在一些显著的区别。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

533

2023.08.11

mysql忘记密码
mysql忘记密码

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。那么忘记mysql密码我们该怎么解决呢?php中文网给大家带来了相关的教程以及其他关于mysql的文章,欢迎大家前来学习阅读。

602

2023.08.14

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

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

精品课程

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

共48课时 | 2万人学习

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

共3课时 | 0.3万人学习

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

共1课时 | 812人学习

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

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