찾다
백엔드 개발PHP 튜토리얼解决php并发读写文件冲突的问题

对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!用一般的文件操作方法完全没有问题。但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失。例如:一个在线聊天室(这里假定把聊天内容写入文件),在同一时刻,用户A和用户B都要操作数据保存文件,首先是A打开了文件,然后更新里面的数据,但这里B也正好也打开了同一个文件,也准备更新里面的数据。当A把写好的文件保存时,这里其实B已经打开了文件。但当B再把文件保存回去时,这里已经造成了数据的丢失,因为这里B用户完全不知道它所打开的文件在它对其进行更改时,A用户也更改了这个文件,所以最后B用户保存更改时,用户A的更新就被会丢失。对于这样的问题,一般的解决方案时当一进程对文件进行操作时,首先对其它进行加锁,意味着这里只有该进程有权对文件进行读取,其它进程如果现在读,是完全没有问题,但如果这时有进程试图想对其进行更新,会遭到操作拒绝,先前对文件进行加锁的进程这时如果对文件的更新操作完毕,这就释放独占的标识,这时文件又恢复到了可更改的状态。接下来同理,如果那个进程在操作文件时,文件没有加锁,这时,它就可以放心大胆的对文件进行锁定,独自享用。所以一般的方案会是:

<p>$fp=fopen('/tmp/lock.txt','w+');</p>if (flock($fp,LOCK_EX)){<br />    fwrite($fp,"Write something here\n");<br />    flock($fp,LOCK_UN);<br />}else{<br />    echo 'Couldn\'t lock the file !';<br />}<br /><p>fclose($fp);</p>

但在PHP中,flock似乎工作的不是那么好!在多并发情况下,似乎是经常独占资源,不即时释放,或者是根本不释放,造成死锁,从而使服务器的cpu占用很高,甚至有时候会让服务器彻底死掉。好像在很多linux/unix系统中,都会有这样的情况发生。所以使用flock之前,一定要慎重考虑。那么就没有解决方案了吗?

其实也不是这样的。如果flock()我们使用得当,完全可能解决死锁的问题。当然如果不考虑使用flock()函数,也同样会有很好的解决方案来解决我们的问题。经过我个人的搜集和总结,大致归纳了解决方案有如下几种。

方案一:对文件进行加锁时,设置一个超时时间。大致实现如下:

<p>if($fp=fopen($fileName,'a')){</p>	$startTime=microtime();<br />	do{<br />		$canWrite=flock($fp,LOCK_EX);<br />		if(!$canWrite){<br />			usleep(round(rand(0,100)*1000));<br />		}<br />	}while((!$canWrite)&&((microtime()-$startTime)<1000));<br />	if($canWrite){<br />		fwrite($fp,$dataToSave);<br />	}<br />	fclose($fp);<br /><p>}</p>

超时设置为1ms,如果这里时间内没有获得锁,就反复获得,直接获得到对文件操作权为止,当然。如果超时限制已到,就必需马上退出,让出锁让其它进程来进行操作。

方案二:不使用flock函数,借用临时文件来解决读写冲突的问题。大致原理如下:

(1)将需要更新的文件考虑一份到我们的临时文件目录,将文件最后修改时间保存到一个变量,并为这个临时文件取一个随机的,不容易重复的文件名。

(2)当对这个临时文件进行更新后,再检测原文件的最后更新时间和先前所保存的时间是否一致。

(3)如果最后一次修改时间一致,就将所修改的临时文件重命名到原文件,为了确保文件状态同步更新,所以需要清除一下文件状态。

(4)但是,如果最后一次修改时间和先前所保存的一致,这说明在这期间,原文件已经被修改过,这时,需要把临时文件删除,然后返回false,说明文件这时有其它进程在进行操作。大致实现代码如下:

<p>$dir_fileopen='tmp';</p>function randomid(){<br />    return time().substr(md5(microtime()),0,rand(5,12));<br />}<br />function cfopen($filename,$mode){<br />    global $dir_fileopen;<br />    clearstatcache();<br />    do{<br />		$id=md5(randomid(rand(),TRUE));<br />        $tempfilename=$dir_fileopen.'/'.$id.md5($filename);<br />    } while(file_exists($tempfilename));<br />    if(file_exists($filename)){<br />        $newfile=false;<br />        copy($filename,$tempfilename);<br />    }else{<br />        $newfile=true;<br />    }<br />    $fp=fopen($tempfilename,$mode);<br />    return $fp?array($fp,$filename,$id,@filemtime($filename)):false;<br />}<br />function cfwrite($fp,$string){<br />	return fwrite($fp[0],$string);<br />}<br />function cfclose($fp,$debug='off'){<br />    global $dir_fileopen;<br />    $success=fclose($fp[0]);<br />    clearstatcache();<br />    $tempfilename=$dir_fileopen.'/'.$fp[2].md5($fp[1]);<br />    if((@filemtime($fp[1])==$fp[3])||($fp[4]==true&&!file_exists($fp[1]))||$fp[5]==true){<br />        rename($tempfilename,$fp[1]);<br />    }else{<br />        unlink($tempfilename);<br />		//说明有其它进程 在操作目标文件,当前进程被拒绝<br />        $success=false;<br />    }<br />    return $success;<br />}<br />$fp=cfopen('lock.txt','a+');<br />cfwrite($fp,"welcome to beijing.\n");<br /><p>fclose($fp,'on');</p>

对于上面的代码所使用的函数,需要说明一下:

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


성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
unset ()와 session_destroy ()의 차이점은 무엇입니까?unset ()와 session_destroy ()의 차이점은 무엇입니까?May 04, 2025 am 12:19 AM

thedifferencebetweenUnset () andsession_destroy () istssection_destroy () thinatesTheentiresession.1) TEREMOVECIFICESSESSION 'STERSESSIVEBLESSESSIVESTIETSTESTERSALLS'SSOVERSOLLS '를 사용하는 것들

로드 밸런싱의 맥락에서 스티커 세션 (세션 친화력)이란 무엇입니까?로드 밸런싱의 맥락에서 스티커 세션 (세션 친화력)이란 무엇입니까?May 04, 2025 am 12:16 AM

stickysessionsureSureSureRequestSaroutEdToTheSERSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESSESINCENSENCY

PHP에서 사용할 수있는 다른 세션 저장 핸들러는 무엇입니까?PHP에서 사용할 수있는 다른 세션 저장 핸들러는 무엇입니까?May 04, 2025 am 12:14 AM

phpoffersvarioussessionsaveAndlers : 1) 파일 : 기본, 단순, 단순한 BUTMAYBOTTLENECKONHIGH-TRAFFICSITES.2) MEMCACHED : 고성능, IdealForspeed-CriticalApplications.3) Redis : SimilartomemCached, WithaddedPersistence.4) 데이터베일 : OffforIntegrati

PHP의 세션은 무엇이며 왜 사용됩니까?PHP의 세션은 무엇이며 왜 사용됩니까?May 04, 2025 am 12:12 AM

PHP의 세션은 여러 요청간에 상태를 유지하기 위해 서버 측의 사용자 데이터를 저장하는 메커니즘입니다. 구체적으로, 1) 세션은 session_start () 함수에 의해 시작되며 데이터는 $ _session Super Global Array를 통해 저장되어 읽습니다. 2) 세션 데이터는 기본적으로 서버의 임시 파일에 저장되지만 데이터베이스 또는 메모리 스토리지를 통해 최적화 할 수 있습니다. 3) 세션은 사용자 로그인 상태 추적 및 쇼핑 카트 관리 기능을 실현하는 데 사용될 수 있습니다. 4) 세션의 보안 전송 및 성능 최적화에주의를 기울여 애플리케이션의 보안 및 효율성을 보장하십시오.

PHP 세션의 수명주기를 설명하십시오.PHP 세션의 수명주기를 설명하십시오.May 04, 2025 am 12:04 AM

phpsessionsStartWithSession_start (), whithesauniqueIdAndCreatesErverFile; thepersistacrossRequestSandCanBemanBledentSandwithSession_destroy ()

절대 세션 타임 아웃의 차이점은 무엇입니까?절대 세션 타임 아웃의 차이점은 무엇입니까?May 03, 2025 am 12:21 AM

절대 세션 시간 초과는 세션 생성시 시작되며, 유휴 세션 시간 초과는 사용자가 작동하지 않아 시작합니다. 절대 세션 타임 아웃은 금융 응용 프로그램과 같은 세션 수명주기의 엄격한 제어가 필요한 시나리오에 적합합니다. 유휴 세션 타임 아웃은 사용자가 소셜 미디어와 같이 오랫동안 세션을 활성화하려는 응용 프로그램에 적합합니다.

세션이 서버에서 작동하지 않으면 어떤 조치를 취 하시겠습니까?세션이 서버에서 작동하지 않으면 어떤 조치를 취 하시겠습니까?May 03, 2025 am 12:19 AM

서버 세션 고장은 다음 단계를 따라 해결할 수 있습니다. 1. 서버 구성을 확인하여 세션이 올바르게 설정되었는지 확인하십시오. 2. 클라이언트 쿠키를 확인하고 브라우저가 지원하는지 확인하고 올바르게 보내십시오. 3. Redis와 같은 세션 스토리지 서비스가 정상적으로 작동하는지 확인하십시오. 4. 올바른 세션 로직을 보장하기 위해 응용 프로그램 코드를 검토하십시오. 이러한 단계를 통해 대화 문제를 효과적으로 진단하고 수리 할 수 ​​있으며 사용자 경험을 향상시킬 수 있습니다.

session_start () 함수의 중요성은 무엇입니까?session_start () 함수의 중요성은 무엇입니까?May 03, 2025 am 12:18 AM

session_start () iscrucialinphpformanagingUsersessions.1) itiniteSanewsessionifnoneexists, 2) ResumesAnxistessions, and3) setSasessionCookieForContInuityAcrosrequests, enablingplicationsirecationSerauthenticationAndpersonalizestContent.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

SublimeText3 Linux 새 버전

SublimeText3 Linux 새 버전

SublimeText3 Linux 최신 버전

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경

DVWA

DVWA

DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

에디트플러스 중국어 크랙 버전

에디트플러스 중국어 크랙 버전

작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.