php中并发读写文件冲突的解决方案 |
本文标签:php高并发,文件冲突 对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!用一般的文件操作方法完全没有问题 。但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失 。 复制代码 代码如下: $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函数,借用临时文件来解决读写冲突的问题 。大致原理如下: 复制代码 代码如下: $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没来得及更新缓存里的文件属性,容易导致访问到最后更新时间不是真实的数据 。所以这里需要使用该函数对已保存的缓存进行清除 。 方案三:对操作的文件进行随机读写,以降低并发的可能性 。 方案四:将所有要操作的进程放入一个队列中 。然后专门放一个服务完成文件操作 。队列中的每一个排除的进程相当于第一个具体的操作,所以第一次我们的服务只需要从队列中取得相当于具体操作事项就可以了,如果这里还有大量的文件操作进程,没关系,排到我们的队列后面即可,只要愿意排,队列的多长都没关系 。 对于以前几种方案,各有各的好处!大致可能归纳为两类: |