php多用户读写文件冲突问题解决办法
发布:smiling 来源: PHP粉丝网 添加日期:2014-08-27 13:50:49 浏览: 评论:0
其实解决多调用同时写一个文件时我们会使用flock来解决些问题,这样同一时间只有一个用户可以写文件,同时其它用户进行等待队列了,下面我来介绍一下flock解决多用户读写文件冲空问题.
所以一般的方案会是,代码如下:
- $fp = fopen("/tmp/lock.txt", "w+");
- if (flock($fp, LOCK_EX)) {
- fwrite($fp, "Write something heren");
- 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 {//开源代码phpfensi.com
- $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,说明文件这时有其它进程在进行操作。
大致实现代码如下:
- $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{//开源代码phpfensi.com
- 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。不需要排队。(影响快)方案三
在设计缓存系统时,一般我们不会采用方案三,因为方案三的分析程序和写入程序是不同步的,在写的时间,完全不考虑到时候分析的难度,只管写的行了,试想一下,如我们在更新一个缓存时,如果也采用随机文件读写法,那么在读缓存时似乎会增加很多流程,但采取方案一、二就完全不一样,虽然写的时间需要等待,当获取锁不成功时,会反复获取,但读文件是很方便的,添加缓存的目的就是要减少数据读取瓶颈,从而提高系统性能.
Tags: php多用户 php读写文件冲突
- 上一篇:php排序算法?php排序经典算法
- 下一篇:php实现事件监听与触发实例程序
推荐文章
热门文章
最新评论文章
- 写给考虑创业的年轻程序员(10)
- PHP新手上路(一)(7)
- 惹恼程序员的十件事(5)
- PHP邮件发送例子,已测试成功(5)
- 致初学者:PHP比ASP优秀的七个理由(4)
- PHP会被淘汰吗?(4)
- PHP新手上路(四)(4)
- 如何去学习PHP?(2)
- 简单入门级php分页代码(2)
- php中邮箱email 电话等格式的验证(2)