2012-07-28 52 views
11

想象一下,你有一個整數以下數組:PHP和億陣列寶寶

array(1, 2, 1, 0, 0, 1, 2, 4, 3, 2, [...]); 

的整數下去高達一萬個條目;而不是硬編碼,它們已經預先生成並存儲在JSON格式的文件中(大小約爲2MB)。這些整數的順序很重要,我不能每次隨機生成它,因爲它應該是一致的,並且始終在相同索引處具有相同的值。

如果該文件是在PHP讀回之後(例如,使用file_get_contents + json_decode)它從700到900毫秒只是爲了讓數組回來 - 「好的」我想,「它可能是合理的,因爲json_decode必須分析2百萬字符,讓我們緩存它「。 APC將其緩存在大約68MB的條目中,可能是正常的,zvals很大。 但是從APC返回這個陣列也需要一些好的600ms這在我眼中仍然太多。

編輯:APC的序列化/反序列化來存儲和檢索一百萬個項目數組是一個漫長而沉重的過程。

所以問題:

  • 我應該想到這個等待時間,如果我打算裝入一萬個條目數組,無論數據存儲或方法,在PHP?據我瞭解APC存儲的zval本身,所以從理論上APC檢索它應該是儘可能快所能得到(沒有分析,沒有轉換,無人接盤)

  • 爲什麼APC這麼慢看起來很簡單的東西?

  • 是否有任何有效的方法來使用PHP在內存中完全加載一百萬個條目數組?假設內存使用率不成問題。

  • 如果我只是根據索引訪問這個數組的切片(例如,將索引15中的數據塊加載到索引76)並且從來沒有將整個數組存儲在內存中(我知道這是一種理智的方法)這樣做,但我想知道所有方面),完整陣列的最有效的數據存儲系統是什麼?顯然不是RDBM;我在想Redis,但我很樂意聽到其他想法。

+1

你試過[SplFixedArray](http://php.net/manual/en/class.splfixedarray.php)嗎? – Buddy 2012-07-28 15:24:05

+0

@Buddy yup,差別不大,可能使用較少的內存,但APC需要的時間相當長。 – Mahn 2012-07-28 15:27:44

+1

如果數字很小並且數組是靜態的,您不能使用單個1Mb字符串對象嗎? – 6502 2012-07-28 15:31:31

回答

3

說整數都是0-15。然後,你可以存儲2每字節:

<?php 
$data = ''; 
for ($i = 0; $i < 500000; ++$i) 
    $data .= chr(mt_rand(0, 255)); 

echo serialize($data); 

運行:php ints.php > ints.ser

現在你有一個包含從0百萬隨機整數一個50萬字節的字符串的文件到15

加載:

<?php 
$data = unserialize(file_get_contents('ints.ser')); 

function get_data_at($data, $i) 
{ 
    $data = ord($data[$i >> 1]); 

    return ($i & 1) ? $data & 0xf : $data >> 4; 
} 

for ($i = 0; $i < 1000; ++$i) 
    echo get_data_at($data, $i), "\n"; 

我的機器上的加載時間約爲0.002秒。

當然,這可能不會直接適用於您的情況,但它將比一百萬個條目的臃腫PHP陣列快得多。坦率地說,擁有一個龐大的PHP數組永遠不是正確的解決方案。

我並不是說這是正確的解決方案,但它絕對可行,如果它適合您的參數。

請注意,如果您的數組在0-255範圍內有整數,您可以擺脫打包並只需訪問數據爲ord($data[$i])。在這種情況下,你的字符串長度將是1M字節。

最後,根據file_get_contents()的文檔,php會將這個文件映射到內存映射。如果是的話,你最好的表現會轉儲原始字節的文件,並使用它像:

$ints = file_get_contents('ints.raw'); 
echo ord($ints[25]); 

這假定ints.raw正好是一百萬字節長。

+0

馬修,感謝我的建議的長期版本。幾點:是的'f_g_s()'在大多數情況下對文件使用mmap(例如,不適用於NFS掛載文件),但它仍然將內容複製到本地分配的字符串中。是的,'ord($ ints [NNN])'訪問生成最有效的操作碼序列。您的2mS是因爲文件內容被VFAT緩存。生產服務器上可能不是這種情況。 – TerryE 2012-07-29 09:04:40

+0

這基本上是我在6502和TerryE建議的時候想象的,但很高興看到它寫出來。那2ms可能是因爲這個文件像@TerryE提到的那樣被緩存了,但是有一個很好的機會可以在APC中緩存,沒有反序列化開銷。我會看看它。 – Mahn 2012-07-30 01:50:20

+0

的確,從APC存儲和檢索它可以輕鬆完成。我會接受這個答案,因爲性能和內存方面的智慧就如同將整個數組存儲在內存中一樣好;我是否應該將整個事情存儲在記憶中是另一個我尚未決定的問題,但同時這會做得更好。 – Mahn 2012-07-30 03:52:24

2

APC存儲序列化的數據,所以它必須在從APC加載回去時被反序列化。這就是你的開銷。

加載它的最有效方式是將文件寫入爲PHP和include(),但對於包含一百萬個元素的數組,將永遠不會達到任何級別的效率......這需要大量的內存,並且需要時間加載。這就是爲什麼要發明數據庫的原因,那麼數據庫的問題是什麼?

編輯

如果你想加快序列化/反序列化,看一看在igbinary擴展

+1

他可能從來沒有聽說過。 – 2012-07-28 15:23:18

+0

是的,我從來沒有聽說過數據庫:)沒有什麼對數據庫,我只是想,因爲數據是靜態的,簡單的生活在像APC一樣的內存中會做得更好;但很遺憾找到APC序列化數據,但我認爲情況並非如此。 – Mahn 2012-07-28 15:27:10

+0

你會發現大多數緩存(APC,memcache,redis等)需要序列化數據,因爲它們被設計爲跨平臺工具,所以不是專門爲PHP數據類型/ zvals設計的。 – 2012-07-28 15:31:12

1

我不能隨機生成它的每一個時間,因爲它應該是一致的,並始終在相同的索引處具有相同的值。

你有沒有讀過僞隨機數字?有這個小東西叫做解決這個問題的種子。

此外,還可以對您的選項和索賠進行基準測試。你有沒有計時file_get_contents與json_decode?在這裏存儲和訪問成本之間需要權衡。例如。如果您的數字是0..9(或0..255),則可能更容易將它們存儲在2Mb字符串中並在其上使用訪問功能。無論是FS還是APC,2Mb的加載速度都會更快。

+0

是的,算法上生成基於固定種子的整數列表是我正在考慮的選項之一,也可能是最優雅的選項之一;我會研究僞隨機數,看看它是否適合我需要的。 – Mahn 2012-07-30 01:41:09

1

正如Mark所說,這就是數據庫創建的原因 - 允許您根據您的常規使用模式有效地搜索(並且操作,但您可能不需要)數據。它也可能比使用數組實現自己的搜索更快。我猜測我們正在談論的是每次訪問數組時,接近2-300MB的數據(在序列化之前)被串行化和反序列化。

如果你想加快速度,試着分別分配數組中的每個元素 - 你可能會花費時間花在串行化上的函數調用開銷。你也可以用你自己的擴展擴展它,把你的數據集包裝在一個小的檢索界面中。

我猜你不能直接存儲zvals的原因是因爲它們包含內部狀態,並且你不能只將變量符號表指向前一個表。