測試PHP幾種方法寫入檔案的效率與安全性

2021-10-25 15:26:40 字數 4612 閱讀 1399

前置條件:

所有測試生成的都寫入乙個新檔案,如果是同乙個檔名,那麼每次執行指令碼前,需要把該日誌檔案刪掉,確保每次執行時日誌檔案都是重新建立的。

每次執行都是往日誌檔案中使用多程序寫入90000行日誌。每種方式分成四種對照組測試:

30*3000 加鎖(即30個程序每個程序寫入3000行,總共90000行,寫入時需對日誌檔案上獨佔鎖)。

30*3000 不加鎖(即30個程序每個程序寫入3000行,總共90000行,寫入時日誌檔案不上鎖)。

90*1000 加鎖(即90個程序每個程序寫入1000行,總共90000行,寫入時需對日誌檔案上獨佔鎖)。

90*1000 不加鎖(即90個程序每個程序寫入1000行,總共90000行,寫入時日誌檔案不上鎖)。

方式一:

加鎖:(n=3000 | n=1000)

for($i=0;$i$msg = "test text";

不加鎖:(n=3000 | n=1000)

for($i=0;$i$msg = "test text";

執**況如下表:

序號程序數

每個程序寫入行數

是否加鎖

第一次執行平均耗時(s)

第二次執行平均耗時(s)

第三次執行平均耗時(s)

1-1y

2.831

2.815

2.861

1-2n

2.826

2.855

2.751

1-3y

2.407

2.396

2.278

1-4n

1.779

2.052

2.01

方式二:

加鎖:(n=3000 | n=1000)

$handle = fopen($log,』a』);

flock($handle,lock_ex);

for($i=0;$i$msg = "test text";

fwrite($handle,$msg);

flock($handle,lock_un);

fclose($handle);

不加鎖:(n=3000 | n=1000)

$handle = fopen($log,』a』);

for($i=0;$i$msg = "test text";

fwrite($handle,$msg);

fclose($handle);

執**況如下表:

序號程序數

寫入行數/每個程序

是否加鎖

第一次執行平均耗時(s)

第二次執行平均耗時(s)

第三次執行平均耗時(s)

2-1y

0.66

0.659

0.658

2-2n

1.272

1.17

1.161

2-3y

0.83

0.855

0.836

2-4n

0.952

1.097

0.947

以方式一跟方式二的**為參照,同一種方式,上不上鎖,效能相差不是很大,從效率上講,方式二要比方式一高效。

最根本的原因是file_put_contents()函式每次執行相當於執行了 fopen(),fwrite(),fclose()三個函式,所以單次執行耗時會比較長。

如果把方式二做個調整,比如把fopen()和fclose都放進for迴圈裡,那麼方式二跟方式一基本沒太大差別。比如下面**:

for($i=0;$i$handle = fopen($log,』a』);

//flock($handle,'lock_ex');

$msg = "test text";

fwrite($handle,$msg);

//flock($handle,'lock_un');

fclose($handle);

當然,如果用這種寫法本身就不合理,還不如直接使用file_put_contents()來的簡單。

不上鎖的情況,日誌寫進去時無序的,各個程序之間穿插著寫入一行日誌。

上鎖的情況,日誌相對有序,基本是乙個程序寫完n行後釋放了獨佔鎖才輪到另乙個程序。但是程序之間也是無序的。比如第乙個子程序寫完,被第5個子程序搶到獨佔鎖,那麼就是第5個子程序先寫,第二個只能繼續等。所以,上鎖的情況同乙個程序寫的日誌才是有序的。

<?php 

set_time_limit(30);

$log = '/data/tmp/a.log';

for($i = 0;$i<30;$i++)

flock($handle,lock_un);

fclose($handle);

unset($handle);

$end = microtime(true);

$s = round($end-$start,3);echo "程序:,開始:,結束:,耗時:".php_eol;

}finallyelse}}

}echo 'over'.php_eol;

系統層面上對每個寫入請求之前的位置更新操作應該具有原子性,且對每個寫操作也是具有完整性保證的。不會導致兩個寫操作交叉執行的情況。

那麼在上鎖的情況下,如果某個子程序在解除檔案鎖之前就掛掉了,會不會導致檔案被鎖死而導致其他程序一直等待呢?

這裡做個測試:開5個子程序,每個程序寫入5行日誌,日誌編號序號(子程序編號:日誌編號)總共25行日誌。

如果在第三個子程序上了獨佔鎖,然後寫入第三行日誌前,讓該子程序退出。具體過程如下:

<?php 

set_time_limit(30);

$log = '/data/tmp/a.log';

for($i = 1;$i<=5;$i++)

$start_time = microtime(true);

//todo 其他業務邏輯

//打點記錄並行任務執行狀況

$fid = posix_getpid();

$ffid = posix_getppid();

$date = date('ymdhis');

$end_time = microtime(true);

$usetime = round($end_time-$start_time,2);

fwrite($handle,$msg);

}flock($handle,lock_un);

fclose($handle);

unset($handle);

$end = microtime(true);

$s = round($end-$start,3);

echo php_eol.$s.',';

//echo "程序:,開始:,結束:,耗時:".php_eol;

}finallyelse}}

}echo 'over'.php_eol;

最終得到得日誌總數時22行,因為第3個子程序只寫了2行就退出了,執行結果如下圖:

由圖可見,就算第三個子程序中途退出了,沒有釋放日誌檔案的獨佔鎖,但是其他程序仍然正常按照獨佔的方式寫入日誌。

原因是當子程序掛掉的時候,該子程序對日誌檔案的獨佔鎖也會被自動解除。所以就算某個子程序上完獨佔鎖,沒來得及解除就退出了,也不用擔心會影響到其他程序對該日誌檔案得使用。

另外,使用 pcntl_fork() 建立程序時需要注意的一些點

pcntl_fork()函式執行的時候,會建立乙個子程序。該子程序會複製當前程序,也就是父程序的所有的變數資料,**,還有狀態。也就是說,在乙個子程序建立之前,定義的變數,常量,函式等,在子程序內都可以使用。

如果建立成功,並且該子程序在父程序內,則返回0,在子程序內返回自身的程序號,失敗則返回-1。

(1)當我們在 for 迴圈 或者 foreach 的迴圈裡建立子程序,那麼在子程序執行的結尾記得將子程序殺死,不然子程序也會進入 for 迴圈和 foreach 迴圈,從而形成遞迴建立子程序的情況。

例如:$arr = array(1,2....n);

foreach($arr as $k=>$v) finally {} 中將子程序殺死,不讓其進入遞迴。

(2)不論時使用for迴圈還是foreach迴圈,都不會按照順序去執行。

比如第(1)部分的兩個例子中,可能最後乙個子程序先執行,最終先進入迴圈遞迴,結果第n個子程序執行了2n次。

而第乙個子程序程序如果最後執行到,就只能執行1次。當然這是在每個子程序執行完沒有殺死的情況。比如:

<?php 

$pid = $fid = posix_getpid();

$arr = array('num1','num2','num3','num4');

foreach($arr as $k=>$v);主程序id:; 父程序id:; 當前程序id:;".php_eol;

echo $msg;

}}?>

結果:

python寫入csv檔案的幾種方法總結

最常用的一種方法,利用pandas包 import pandas as pd 任意的多組列表 a 1,2,3 b 4,5,6 字典中的key值即為csv中列名 dataframe pd.dataframe 將dataframe儲存為csv,index表示是否顯示行名,default true dat...

python寫入csv檔案的幾種方法總結

最常用的一種方法,利用pandas包 import pandas as pd 任意的多組列表 a 1,2,3 b 4,5,6 字典中的key值即為csv中列名 dataframe pd.dataframe 將dataframe儲存為csv,index表示是否顯示行名,default true dat...

php 讀取檔案的幾種方法

檔案操作的三個步驟,開啟,操作,關閉。fopen fopen 路徑,方式 fwrite fopen,寫入的字串 fclose fopen 其中開啟方式有如下幾種方式 模式描述 r唯讀。在檔案的開頭開始。r 讀 寫。在檔案的開頭開始。w只寫。開啟並清空檔案的內容 如果檔案不存在,則建立新檔案。w 讀 ...