>  기사  >  백엔드 개발  >  PHP 다중 사용자 읽기 및 쓰기 파일 충돌을 해결하는 방법

PHP 다중 사용자 읽기 및 쓰기 파일 충돌을 해결하는 방법

伊谢尔伦
伊谢尔伦원래의
2017-06-24 14:51:101188검색

파일을 작성하기 위해 여러 호출이 이루어질 때 일부 문제를 해결하기 위해 무리를 사용하여 한 명의 사용자만 동시에 파일을 작성할 수 있고 다른 사용자는 대기열에서 기다리고 있습니다. 다중 사용자 읽기 및 쓰기 파일이 비어 있는 문제를 해결하기 위해 무리를 짓습니다

일반적인 해결책은 다음과 같습니다:

$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에서는 무리가 그렇게 잘 작동하지 않는 것 같습니다! 다중 동시성의 경우 리소스가 독점되어 즉시 해제되지 않거나 전혀 해제되지 않는 경우가 많아 교착상태를 일으키고, 서버의 CPU 사용량이 매우 높아지며, 때로는 서버가 완전히 죽는 경우도 있는 것으로 보입니다. 이는 많은 Linux/Unix 시스템에서 발생하는 것 같습니다.
그래서 플록을 사용하기 전에 신중하게 생각하셔야 합니다.
그럼 해결책은 없나요? 실제로는 그렇지 않습니다. 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로 설정됩니다. 이 시간 내에 잠금을 얻지 못하면 다음 권한까지 반복적으로 획득됩니다. 파일을 확실히 얻었습니다. 시간 초과 제한에 도달한 경우 즉시 종료하고 다른 프로세스가 작동할 수 있도록 잠금을 포기해야 합니다.
옵션 2: 무리 기능을 사용하지 말고 임시 파일을 사용하여 읽기 및 쓰기 충돌 문제를 해결하세요.
일반적인 원칙은 다음과 같습니다.
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(&#39;lock.txt&#39;,&#39;a+&#39;);
cfwrite($fp,"welcome to beijing.n");
fclose($fp,&#39;on&#39;);

对于上面的代码所使用的函数,需要说明一下:
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.