我们已经准备好了,你呢?

2024我们与您携手共赢,为您的企业形象保驾护航!

对于日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()函数,也会有很好的方案来解决我们的问题。经过我个人的收集和总结,我总结了以下几种解决办法。

解决方案 1:锁定文件时设置超时时间。

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.();重命名文件或目录。该函数比较像Linux中的mv,可以很方便的更新文件或目录的路径或名称。

但是当我测试上面的代码时,如果新文件名已经存在,它会给出一个错误,说当前文件已经存在。但它在Linux上运行良好。

2.();清除文件状态。PHP 会缓存所有文件属性信息以提供更高的性能,但有时候,当多个进程删除或更新文件时,PHP 没有来得及更新缓存中的文件属性,这可能会导致访问到的最后更新时间不是真实的数据。所以这里需要使用这个函数来清除保存的缓存。

解决方案三:对需要操作的文件进行随机读写,减少并发的可能性。

这个方案好像在记录用户访问日志的时候用的比较多。

前面我们需要定义一个随机空间,这个空间越大,并发的可能性就越小,这里我们假设随机读写空间为[1-500],那么我们的日志文件的分布就会在log1~~之间,每次用户访问的时候,数据都会被随机写入到log1~~之间的任意一个文件中。

同时有两个进程在记录日志,进程A可能更新log32文件,那进程B呢?这时候更新的可能性就是。要知道如果进程B要操作log32的话,这个概率基本就是1/500,几乎为零。

当我们需要分析访问日志的时候,只需要先将日志合并,然后进行分析即可。

采用这种方案记录日志的一个好处是,进程操作排队的可能性比较小,使得进程能够非常快速的完成每个操作。

方案四:把需要操作的所有进程放入一个队列,然后放入一个专门的服务来完成文件操作。

队列中每一个被排除的进程都相当于第一个具体的操作,所以第一次我们的服务只需要从队列中获取相当于具体操作项即可。如果这里还有大量的文件操作进程,那也没关系,直接放到我们队列后面就行了。只要你愿意排队,队列多长都无所谓。

对于前面几种方案,各有各的优点!大致可以归纳为两类:

1.需要排队(影响缓慢)如方案1、2、4

2. 无需排队。(效果立竿见影)选项 3

在设计缓存系统的时候,我们一般不采用方案三,这是因为方案三的分析程序和写入程序是不同步的,写入的时候完全不考虑分析的难度,只做写入。试想一下,当我们更新一个缓存的时候,如果也采用随机文件读写的方式,读缓存的时候好像会增加很多进程。但是方案一和方案二就完全不一样了,虽然写入的时候需要等待(没有成功获取锁的时候会重复获取),但是读取文件的时候却非常方便。增加缓存的目的是为了减少数据读取的瓶颈,从而提高系统性能。

以上是我个人经验总结和一些信息,如果有错误或者没有提到的地方,欢迎大家指出。

二维码
扫一扫在手机端查看

本文链接:https://by928.com/5893.html     转载请注明出处和本文链接!请遵守 《网站协议》
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。

项目经理在线

我们已经准备好了,你呢?

2020我们与您携手共赢,为您的企业形象保驾护航!

在线客服
联系方式

热线电话

13761152229

上班时间

周一到周五

公司电话

二维码
微信
线