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

一般的方案会是:

复制代码 代码如下:

$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 {
         $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{
        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、不需要排队。(影响快)方案三
在设计缓存系统时,一般我们不会采用方案三。因为方案三的分析程序和写入程序是不同步的,在写的时间,完全不考虑到时候分析的难度,只管写的行了。试想一下,如我们在更新一个缓存时,如果也采用随机文件读写法,那么在读缓存时似乎会增加很多流程。但采取方案一、二就完全不一样,虽然写的时间需要等待(当获取锁不成功时,会反复获取),但读文件是很方便的。添加缓存的目的就是要减少数据读取瓶颈,从而提高系统性能。

时间: 2024-11-02 19:01:54

php多用户读写文件冲突的解决办法_php技巧的相关文章

phpmyadmin导入(import)文件限制的解决办法_php技巧

到网上搜索了一下前人的解决办法,大多数都说修改php.ini中的upload_max_filesize,但修改了这个以后,还是提示这个问题:但phpmyadmin在提示这个问题的时候,右下角有一行英文提示,大致意思是说,解决这个问题,可以参考 phpmyadmin文档:直接点击这个链接,phpmyadmin自动查找到了以下说明: [1.16] I cannot upload big dump files (memory, http or timeout problems). Starting w

PHP判断上传文件类型的解决办法_php技巧

分享给大家php判断上传文件类型的方法,大家一起学习学习. /** * 读取文件前几个字节 判断文件类型 * @return String */ function checkTitle($filename){ $file=fopen($filename, "rb"); $bin=fread($file, 2); //只读2字节 fclose($file); $strInfo =@unpack("c2chars", $bin); $typeCode=intval($s

浏览器预览PHP文件时顶部出现空白影响布局分析原因及解决办法_php技巧

在编写PHP文件过程中,发现在浏览器预览PHP文件时,顶部会出现一行空白,影响了页面的布局. 关于BOM header的解释如下: 通常情况下,使用Windows系统自带的记事本程序编写网页程序,但在编写或修改php博客系统代码后,进行调试时总是会出现如同以下几点问题: –不能登入或者不能登出: –页顶出现一条空白: –页顶出现错误警告: –其它不正常的情况. 分析原因: 由于使用UTF-8编码,在编写或修改代码后都保存为utf-8编码格式.虽然现在几乎所有的文本编辑软件都可以显示并编辑UTF-

PHP页面转UTF-8中文编码乱码的解决办法_php技巧

对于乱码这个问题php开发者几乎都会有碰到过,我们下面主要是介绍了php文件乱码和页面乱码. PHP页面转UTF-8编码问题 1.在代码开始出加入一行:header("Content-Type: text/html;charset=utf-8"); 2.PHP文件编码问题 点击编辑器的菜单:"文件"->"另存为",可以看到当前文件的编码,确保文件编码为:UTF-8, 如果是ANSI,需要将编码改成:UTF-8. 3.PHP文件头BOM问题:

基于python发送邮件的乱码问题的解决办法_php技巧

公司项目中需要通过后台发送邮件,邮件内容包括图片附件.如果通过PHPmailer发送,由于邮件服务器可能存在延迟现象,通过PHPmailer发送邮件,需要等待邮件发送成功后才能返回结果,这在实践中证明,有时发送邮件无法即时返回结果,影响用户体验. 于是我通过python发送邮件,PHP通过调用脚本方式来调用,这样执行脚本成功后立即返回,而无需判断邮件是否发送成功.只要成功执行脚本文件即向客户端返回成功标志.这样极大的提高了邮件发送速度,保证良好的用户体验效果. 但是,在通过python发送邮件,

php提示无法加载或mcrypt没有找到 PHP 扩展 mbstring解决办法_php技巧

没有找到 PHP 扩展 mbstring,而您现在好像在使用多字节字符集.没有 mbstring 扩展的 phpMyAdmin 不能正确分割字符串,可能产生意想不到的结果. 无法加载mcrypt的解决办法 提示:无法加载mcrypt (外链,英语) 扩展,请检查您的 PHP 配置. 在C:\WINDOWS目录下的php.ini文件中, 没有将";extension=php_mcrypt.dll"中的前面一个";"去掉 所以不能使用相应功能,解决方法是打开php.in

php Sql Server连接失败问题及解决办法_php技巧

1.确认数据库服务开启状态 2.php.ini配置中的扩展打开 3.检查数据库相关的版本 (1)Sql2000此时要检查php目录和apache的bin目录下的ntwdblib.dll文件的版本是否符合,右键点击ntwdblib.dll看文件属性就可以看见版本.Sql2000对应的ntwdblib.dll应该是7.0的版本 (2)Sql2005的时候ntwdblib.dll对应的是8.0的版本. 如果版本不对可能出现链接失败,仔细检查即可. 本人在链接sql2005的时候遇到的问题,经过仔细排查

关于svn冲突的解决方法_php技巧

1.在冲突文件上右键----edit conflicts-----然后手动修改文件冲突的红色地方,其他地方可以不用管. 2.修改完后保存.将本地和svn里面的文件都保存好. 3.再在冲突的文件上面点击右键-----resolved,在弹出的窗口中点击相应文件并点击确定. 4.注意,这个时候并没有提交成功.这个时候只是说你已经将两个版本的文件改一致了.冲突的部分被你解决了,但是还没有将本地文件提交到svn里.所以,现在再点击文件右键----update.没有错误了就再在文件上右键-----comm

PHP提示Notice: Undefined variable的解决办法_php技巧

PHP默认配置会报这个错误,我的PHP版本是5.2.13,存在这个问题: Notice: Undefined variable 这就是将警告在页面上打印出来,虽然这是有利于暴露问题,但实现使用中会存在很多问题. 需要设置显示错误级别,来解决问题. 网络上的通用解决办法是修改php.ini的配置: 解决方法: 1) error_reporting设置: 找到error_reporting = E_ALL 修改为error_reporting = E_ALL & ~E_NOTICE 2) regis