`
zhangchibang
  • 浏览: 344976 次
社区版块
存档分类
最新评论

php中,高并发状态下文件的读写

    博客分类:
  • PHP
阅读更多


对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!!用一般的文件操作方法完全没有问题。但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失。
例如:一个在线聊天室(这里假定把聊天内容写入文件),在同一时刻,用户A和用户B都要操作数据保存文件,首先是A打开了文件,然后更新里面的数据,但这里B也正好也打开了同一个文件,也准备更新里面的数据。当A把写好的文件保存时,这里其实B已经打开了文件。但当B再把文件保存回去时,这里已经造成了数据的丢失,因为这里B用户完全不知道它所打开的文件在它对其进行更改时,A用户也更改了这个文件,所以最后B用户保存更改时,用户A的更新就被会丢失。
对于这样的问题,一般的解决方案时当一进程对文件进行操作时,首先对其它进行加锁,意味着这里只有该进程有权对文件进行读取,其它进程如果现在读,是完全没有问题,但如果这时有进程试图想对其进行更新,会遭到操作拒绝,先前对文件进行加锁的进程这时如果对文件的更新操作完毕,这就释放独占的标识,这时文件又恢复到了可更改的状态。接下来同理,如果那个进程在操作文件时,文件没有加锁,这时,它就可以放心大胆的对文件进行锁定,独自享用。
所以一般的方案会是:



$fp = fopen ( "/tmp/lock.txt", "w+" );
if (flock ( $fp, LOCK_EX )) {
	fwrite ( $fp, "Write something here\n" );
	flock ( $fp, LOCK_UN );
} else {
	echo "Couldn't lock the file !";
}
fclose ( $fp );

 


但在PHP中,flock似乎工作的不是那么好!在多并发情况下,似乎是经常独占资源,不即时释放,或者是根本不释放,造成死锁,从而使服务器的cpu占用很高,甚至有时候会让服务器彻底死掉。好像在很多linux/unix系统中,都会有这样的情况发生。
所以使用flock之前,一定要慎重考虑。
那么就没有解决方案了吗?其实也不是这样的。如果flock()我们使用得当,完全可能解决死锁的问题。当然如果不考虑使用flock()函数,也同样会有很好的解决方案来解决我们的问题。
经过我个人的搜集和总结,大致归纳了解决方案有如下几种。
方案一:对文件进行加锁时,设置一个超时时间.
大致实现如下:



if ($fp = fopen ( $fileName, 'a' )) {
	$startTime = microtime ();
	do {
		$canWrite = flock ( $fp, LOCK_EX );
		if (! $canWrite)
			usleep ( round ( rand ( 0, 100 ) * 1000 ) );
	} while ( (! $canWrite) && ((microtime () - $startTime) < 1000) );
	if ($canWrite) {
		fwrite ( $fp, $dataToSave );
	}
	fclose ( $fp );
}

 


超时设置为1ms,如果这里时间内没有获得锁,就反复获得,直接获得到对文件操作权为止,当然。如果超时限制已到,就必需马上退出,让出锁让其它进程来进行操作。
方案二:不使用flock函数,借用临时文件来解决读写冲突的问题。
大致原理如下:
1。将需要更新的文件考虑一份到我们的临时文件目录,将文件最后修改时间保存到一个变量,并为这个临时文件取一个随机的,不容易重复的文件名。
2。当对这个临时文件进行更新后,再检测原文件的最后更新时间和先前所保存的时间是否一致。
3。如果最后一次修改时间一致,就将所修改的临时文件重命名到原文件,为了确保文件状态同步更新,所以需要清除一下文件状态。
4。但是,如果最后一次修改时间和先前所保存的一致,这说明在这期间,原文件已经被修改过,这时,需要把临时文件删除,然后返回false,说明文件这时有其它进程在进行操作。
大致实现代码如下:



<?php
$dir_fileopen = "tmp";

function randomid() {
	return time () . substr ( md5 ( microtime () ), 0, rand ( 5, 12 ) );
}
function cfopen($filename, $mode) {
	global $dir_fileopen;
	clearstatcache ();
	do {
		$id = md5 ( randomid ( rand (), TRUE ) );
		$tempfilename = $dir_fileopen . "/" . $id . md5 ( $filename );
	} while ( file_exists ( $tempfilename ) );
	if (file_exists ( $filename )) {
		$newfile = false;
		copy ( $filename, $tempfilename );
	} else {
		$newfile = true;
	}
	$fp = fopen ( $tempfilename, $mode );
	return $fp ? array ($fp, $filename, $id, @filemtime ( $filename ) ) : false;
}
function cfwrite($fp, $string) {
	return fwrite ( $fp [0], $string );
}
function cfclose($fp, $debug = "off") {
	global $dir_fileopen;
	$success = fclose ( $fp [0] );
	clearstatcache ();
	$tempfilename = $dir_fileopen . "/" . $fp [2] . md5 ( $fp [1] );
	if ((@filemtime ( $fp [1] ) == $fp [3]) || ($fp [4] == true && ! file_exists ( $fp [1] )) || $fp [5] == true) {
		rename ( $tempfilename, $fp [1] );
	} else {
		unlink ( $tempfilename );
		//说明有其它进程 在操作目标文件,当前进程被拒绝
		$success = false;
	}
	return $success;
}
$fp = cfopen ( 'lock.txt', 'a+' );
cfwrite ( $fp, "welcome to beijing.\n" );
fclose ( $fp, 'on' );

 


对于上面的代码所使用的函数,需要说明一下:
1.rename();重命名一个文件或一个目录,该函数其实更像linux里的mv。更新文件或者目录的路径或名字很方便。
但当我在window测试上面代码时,如果新文件名已经存在,会给出一个notice,说当前文件已经存在。但在linux下工作的很好。
2.clearstatcache();清除文件的状态.php将缓存所有文件属性信息,以提供更高的性能,但有时,多进程在对文件进行删除或者更新操作时,php没来得及更新缓存里的文件属性,容易导致访问到最后更新时间不是真实的数据。所以这里需要使用该函数对已保存的缓存进行清除。

方案三:对操作的文件进行随机读写,以降低并发的可能性。
在对用户访问日志进行记录时,这种方案似乎被采用的比较多。
先前需要定义一个随机空间,空间越大,并发的的可能性就越小,这里假设随机读写空间为[1-500],那么我们的日志文件的分布就为log1~到log500不等。每一次用户访问,都将数据随机写到log1~log500之间的任一文件。
在同一时刻,有2个进程进行记录日志,A进程可能是更新的log32文件,而B进程呢?则此时更新的可能就为log399.要知道,如果要让B进程也操作log32,概率基本上为1/500,差不多约等于零。
在需要对访问日志进行分析时,这里我们只需要先将这些日志合并,再进行分析即可。
使用这种方案来记录日志的一个好处时,进程操作排队的可能性比较小,可以使进程很迅速的完成每一次操作。

方案四:将所有要操作的进程放入一个队列中。然后专门放一个服务完成文件操作。
队列中的每一个排除的进程相当于第一个具体的操作,所以第一次我们的服务只需要从队列中取得相当于具体操作事项就可以了,如果这里还有大量的文件操作进程,没关系,排到我们的队列后面即可,只要愿意排,队列的多长都没关系。

对于以前几种方案,各有各的好处!大致可能归纳为两类:
1。需要排队(影响慢)比如方案一、二、四
2。不需要排队。(影响快)方案三
在设计缓存系统时,一般我们不会采用方案三。因为方案三的分析程序和写入程序是不同步的,在写的时间,完全不考虑到时候分析的难度,只管写的行了。试想一下,如我们在更新一个缓存时,如果也采用随机文件读写法,那么在读缓存时似乎会增加很多流程。但采取方案一、二就完全不一样,虽然写的时间需要等待(当获取锁不成功时,会反复获取),但读文件是很方便的。添加缓存的目的就是要减少数据读取瓶颈,从而提高系统性能。

从上为个人经验和一些资料的总结,有什么不对的地方,或者没有谈到的地方,欢迎各位同学指正。

分享到:
评论

相关推荐

    php中并发读写文件冲突的解决方案

    在PHP开发中,高并发环境下对文件进行读写操作时,常常会遇到数据丢失的问题。这主要是因为多个进程或线程同时对同一个文件进行操作,而没有相应的机制来控制对文件的访问。当一个进程正在写入数据时,如果另一个...

    毕业设计:基于PHP高并发高性能秒杀系统.zip

    【标题】基于PHP高并发高性能秒杀系统的设计与实现 在现代互联网环境中,秒杀活动是一种常见的营销策略,它能够短时间内吸引大量用户参与,对系统的并发处理能力和性能提出了极高的要求。基于PHP构建的高并发高性能...

    高并发高访问量网站的运维技术[定义].pdf

    在Web开发中,如Java、PHP等语言都提供了缓存模块,例如Java的内存缓存机制,PHP的Pear Cache库,帮助处理高并发情况下的数据交互。 5. **CDN(Content Delivery Network)**: CDN是一种分布式网络服务,通过将网站...

    php多用户读写文件冲突的解决办法

    在处理PHP多用户读写同一文件时,文件锁定机制是确保数据一致性的关键。flock是PHP中用于文件锁定的函数,...在任何情况下,都需要仔细考虑数据的一致性和系统的稳定性,确保在高并发情况下能够正确处理文件读写冲突。

    PHP使用文件锁解决高并发问题示例

    这在处理共享资源时非常有用,尤其是在高并发环境下,如数据库操作、文件读写等。 ### 二、PHP中的文件锁 PHP提供了`flock()`函数来实现文件锁。该函数接受两个参数:文件句柄和锁定类型。锁定类型可以是以下几种...

    基于PHP的bt种子程序源码 php版.zip

    9. 负载测试和调试:在项目完成后,需要进行负载测试,确保在高并发环境下也能正常运行。此外,调试工具如Xdebug可以帮助找出代码中的问题。 10. 版本控制:使用版本控制系统如Git,可以方便地管理代码的迭代和协作...

    大型并发系统整理版

    本篇文章将根据给定文件中的内容,深入探讨高并发系统设计中的一些关键技术点,包括缓存策略、性能优化方法以及具体的技术选型。 #### 二、缓存的重要性 在高并发系统的设计中,缓存是至关重要的。正如Cal ...

    php投票系统(ajax实现)

    对于高并发场景,文件操作可能成为瓶颈,可以考虑引入缓存机制,如Memcached或Redis,以提高数据读写速度。同时,合理设计投票逻辑,避免不必要的计算和IO操作。 总结来说,这个基于PHP和Ajax的投票系统展示了如何...

    php session实现多级目录存放实现代码

    通过上述的配置和目录结构优化,可以有效提高服务器处理Session文件的性能,尤其在高并发访问的情况下,能够使服务器更加高效地管理和存储Session数据。需要注意的是,改变Session存储的方式可能会影响到Session的...

    PHP电商项目1

    4. **用户体验**:优化前端页面加载速度,采用CDN加速静态资源的分发,提高用户在高并发下的访问体验。 5. **后台管理**:设计友好的后台管理系统,方便商家进行商品上架、订单处理、库存管理等操作。 6. **支付...

    php图片上传类.zip

    高效的图片处理和上传策略对于高并发的网站至关重要。类可能包含了多线程处理、异步上传等技术,以提升系统性能。 10. **兼容性** 良好的图片上传类应考虑到不同的PHP版本和服务器环境,保证在不同环境下都能稳定...

    php.rar_php计数器程式

    5. 可扩展性:如果需要处理大量访问,简单的文本计数器可能无法满足需求,因为频繁的文件读写操作会影响性能。此时,可以考虑使用缓存技术(如Memcached或Redis)或者数据库来存储计数器,以提高效率。 6. 用户界面...

    php mysql redis nginx memcached

    标题和描述中提到的"php mysql redis nginx memcached"是一组常见的Web开发技术组合,用于构建高性能、可扩展的Web应用程序。以下是对这些技术的详细解释: 1. PHP(Hypertext Preprocessor):PHP是一种广泛使用的...

    基于PHP的ABC网络硬盘带提取码PHP版utf8源码.zip

    8. 性能优化:为了处理大量文件和高并发访问,可能需要实现缓存机制(如Memcached或Redis)、文件分块上传/下载、负载均衡等策略,以提高系统性能。 9. 响应式设计:适应不同设备的屏幕尺寸和操作系统,如桌面、...

    MyProber PHP探针

    MongoDB是一个分布式文档型数据库,常用于处理大数据量、高并发的场景,而Redis则是一款高性能的键值对内存数据库,适用于快速读写操作和数据缓存。MyProber能有效监控这两者,可以提供关于连接状态、查询性能、内存...

    php简易网上投票系统

    8. **性能优化**:虽然这是一个简易系统,但考虑到高并发访问,可能需要考虑缓存技术(如文件缓存或内存缓存)以减轻服务器压力,以及适当的资源管理以提高响应速度。 9. **版本控制**:看到有“pro6”这样的文件名...

    基于php的webdav协议的项目

    - 调试和优化性能,确保服务器在高负载下也能正常运行。 总的来说,"基于php的webdav协议的项目"提供了一个学习WebDAV协议和PHP服务器端开发的实践平台,对于想要深入理解HTTP协议和分布式文件系统操作的开发者来说...

    多php服务器实现多session并发运行

    同时,考虑到PHP在高并发下的性能,可以优化数据库连接、使用负载均衡技术、调整服务器配置等措施,以提升系统的稳定性和可扩展性。 此外,对于PHP并发操作,比如并发读写文件、并发访问MySQL等,可能需要采取额外...

    常见php数据文件缓存类汇总

    6. 缺点与优化:文件缓存相对于内存缓存(如Memcache)来说,存取速度较慢,且在高并发情况下可能会成为性能瓶颈。此外,频繁地进行文件读写操作可能会增加服务器的I/O负担。因此,在实际应用中,可以根据具体情况...

Global site tag (gtag.js) - Google Analytics