有大量檔案需要處理。
用一個php進程去操作會很慢。
如何在一個進程讀某一個檔案的時候,把檔案鎖上。
不讓其他進程可以再讀而直接跳過,繼續讀其他的?
把正在讀得檔案rename,讀完之後再rename回來,效率挺低的。如果實在沒有更好的方法,就只能用這個了。
flock,測試了一下,貌似不太好用,試了一下,沒實現檔案的非阻塞讀鎖。
給不同進程分配不同的檔案,不好實現。
也沒資料庫。就算有。用資料庫做鎖,貌似比rename更低效。
請問有什麼更好的方式可以給檔案加 讀鎖。因為,只需要讀檔案。
回複內容:
有大量檔案需要處理。
用一個php進程去操作會很慢。
如何在一個進程讀某一個檔案的時候,把檔案鎖上。
不讓其他進程可以再讀而直接跳過,繼續讀其他的?
把正在讀得檔案rename,讀完之後再rename回來,效率挺低的。如果實在沒有更好的方法,就只能用這個了。
flock,測試了一下,貌似不太好用,試了一下,沒實現檔案的非阻塞讀鎖。
給不同進程分配不同的檔案,不好實現。
也沒資料庫。就算有。用資料庫做鎖,貌似比rename更低效。
請問有什麼更好的方式可以給檔案加 讀鎖。因為,只需要讀檔案。
你的問題是:
1. 很多檔案,想多進程處理,以提高效率,縮短總處理時間
2. 這些進程只需要讀檔案,不需要寫
3. 對每個檔案,只要有一個進程處理過它就可以了,沒有多個進程都必須處理它的需求
你的需求其實是分治,將檔案分為多個組(不一定要在檔案系統上建立目錄),然後分而治之,這種情況不需要用鎖.
鎖不是用於這種情境的,鎖用於下面這種情境:
1. 檔案file.txt裡面記錄了user1的銷售額和user2的銷售額,user1+user2的銷售總額
2. 進程php1負責寫入user1的資料,進程php2負責寫入user2的資料,兩個進程各讀出銷售總額顯示給user1,user2
3. user1和user2同時要求寫入,真的是同時,不是前後差個幾秒什麼的
建議你這樣解決:
1. 啟動多個PHP進程(nohup php your_script.php your_dir &)
2. 每個PHP進程賦予一個序號(假設4個進程,那就0,1,2,3),可以通過對進程自身的pid模運算取餘數得到,也可以在啟動進程的時候通過命令列傳入,隨你了
3. 每個進程在處理檔案前先對檔案名稱做crc32()運算,模一下進程總數: crc32(file_name) % 4, 模數結果與此進程的序號相等就讀取內容並處理,不相等就跳過
最後:小編幫我排個版吧...
- 如@felix021 所說,flock($res, LOCK_EX|LOCK_NB) 是有效,請好好看文檔……
- @賣掉內褲去上網 所說,memcached 雖然是純記憶體操作,但畢竟有網路或unix domain socket開銷,為了一個檔案鎖去啟動一個Memcached未免太浪費。Linux中可以使用共用記憶體來做鎖,請參考php手冊中 shm_has_var / shm_put_var 。
- 如你自己說的,每進程分配一個專有檔案也是可以的,並不是很麻煩,如果所有背景工作處理序都有一個主進程fork出來就更方便了,最簡單最dirty的辦法是,把檔案名稱放在主進程數組裡,每次fork之前,就pop出來一個檔案名稱……
- 用檔案rename的方法跟@賣掉內褲去上網 說的依靠判斷一個lock檔案存在與否的辦法開銷差不多,如果你的檔案數不是很多,鎖搶佔不頻繁,可以這麼做……
除了檔案鎖以外,其他自行實現的鎖在有鎖進程意外退出時,都需要自行實現解鎖機制。所以,還是推薦用檔案鎖,會由系統來自動釋放……
關於flock的示範
function do_flock(){ ob_implicit_flush(true); //關閉PHP輸出緩衝 $file = __FILE__; $f = fopen($file, 'r'); $count = 0; while(1){ $locked = flock($f, LOCK_NB | LOCK_EX); if($locked) { echo "GOT LOCK\n"; sleep(10); flock($f, LOCK_UN); echo "RELEASE LOCK\n"; break; } else { echo 'LOCKED BY OTHER, WAIT:' . ($count ++) . "\n"; sleep(1); } } }
測試方法:
time curl --no-buffer "http://localhost/flock"//在10秒鐘之內另外一個terminal裡再執行相同命令
Terminal 1 輸出:
GOT LOCKRELEASE LOCKreal0m10.023suser0m0.008ssys0m0.008s
Terminal 2 輸出:
LOCKED BY OTHER, WAIT:0LOCKED BY OTHER, WAIT:1LOCKED BY OTHER, WAIT:2LOCKED BY OTHER, WAIT:3LOCKED BY OTHER, WAIT:4LOCKED BY OTHER, WAIT:5LOCKED BY OTHER, WAIT:6LOCKED BY OTHER, WAIT:7LOCKED BY OTHER, WAIT:8GOT LOCKRELEASE LOCKreal0m19.025suser0m0.008ssys0m0.008s
Ubuntu 12.04 測試通過,沒有Mac沒法測,但應該沒啥問題,畢竟是同根同源的,PHP源碼裡也只是對Win有特殊實現……
注意,以下情況會影響輸出效果
- 瀏覽器有渲染緩衝,webkit核心的瀏覽器大概需要額外輸出4k位元組的空白才會開始渲染輸出
- 如果用FastCGI方式部署的PHP,Web伺服器可能會有輸出緩衝,我用的Cherokee大概也是4k左右的緩衝
針對這些緩衝,可以在每次echo時,把內容用str_pad補齊4096位元組
1. 為什麼你會覺得rename效率低呢?如果一個目錄下的檔案不是相當多的話,這個應該不低。
2. 你肯定沒有好好看flock的文檔.
如果不希望 flock() 在鎖定時堵塞,則給 operation 加上 LOCK_NB(PHP 4.0.1 以前的版本中設定為 4)。
用memcached實現吧。
比如讀取檔案 $filename = "t.txt";
if(!$memcached->get($filename)){ //檔案鎖不存在,那麼執行檔案讀取功能 //首先再將檔案鎖住, $memcaced->save($filename,'1'); $fs = fopen($filename,'r+'); fclose($fs); //讀取完畢釋放檔案鎖 $memcaced->delete($filename);}else{ // 檔案鎖已經存在,跳過 }
以上是 memcaced 純記憶體操作,速度會很快,根本不要考慮到效能的問題,當然還有一種方法,採用真正的檔案鎖,即添加一個新檔案的方法控制,檔案爭用,但是此方法將加大IO的開銷。
flock,測試了一下,貌似不太好用,試了一下,沒實現檔案的非阻塞讀鎖。
我記得好像有啊
http://php.net/manual/zh/function.flo...
LOCK_SH 就是讀取鎖,加鎖後,其他程式可以讀取,但不能寫入
LOCK_EX 就是寫入鎖,加鎖後,其他程式不能讀也不能寫
LOCK_NB(Windows不支援) 就是非阻塞模式,得不到鎖立刻返回
我覺得這三個參數組合起來完全可以實現樓主的需求.
最簡單解決辦法,一共倆檔案,一個write,一個read。reader完了等writer close了就對調檔案名稱。