2017-09-26 137 views
1

我在PHP中有一個遞歸函數,它在一個API中循環,它只允許您一次恢復200條記錄。在PHP中減少遞歸函數的內存使用

但是由於此API具有非常高的響應延遲,我們決定使用本地中間數據庫添加這些記錄,並在網站上顯示相同內容。

但是,由於這個API有超過30000條記錄,遞歸函數消耗了大量內存,因爲它在30000條記錄的情況下,它必須被遞歸調用超過1500次,並且它最終賦予着名堆棧溢出。

我不知道是否有一種手動的方式來清除這個函數的內存,通過再次調用它而不會丟失它的值。

代碼示例:

public function recursive ($index = 0, $offset = 200) { 
    $api = GetConectApi ($index, offset)-> Getrecords(); 
    foreach ($api $value) { 
     \\Here is my necessary loop 
    } 
    if (count ($API) > 0) { 
     $this Recursive ($index + 200, $offset + 200); 
    } 
} 

我想找到一種方法,當它被稱爲遞歸函數再次取消了先前的分配,而不會丟失已傳遞的參考值。

+0

你不是在使用緩存嗎? –

+0

嗨,謝謝你的回答,緩存在第一次交互循環完成後對我有用,而且這個檢查每天發生一次,所以無論使用緩存,腳本每天都會運行1次,以使可能的更新日誌 –

+1

這可能屬於[代碼審查SE](https://codereview.stackexchange.com/) –

回答

1

要通過創建擴大user3720435's answer,您正在使用大量的內存每次運行該功能時都會有新的$api變量。要理解爲什麼,我們的「展開」的代碼 - 想象這是所有寫出來的順序沒有函數調用:

$api1 = GetConectApi ($index1, offset1)-> Getrecords(); 
foreach ($api1 => $value1) { 
    // Here is my necessary loop 
} 
if (count ($api1) > 0) { 
    // RECURSION HAPPENS HERE 
    $index2 = $index1 + 200, $offset2 = $offset1 + 200 
    $api2 = GetConectApi ($index, offset)-> Getrecords(); 
    foreach ($api2 => $value2) { 
     // Here is my necessary loop 
    } 
    if (count ($api2) > 0) { 
     // RECURSE AGAIN, AND AGAIN, AND AGAIN 
    } 
} 

請注意,我已經改名爲所有的變量爲$api1$api2,等等。這是因爲每個當你運行該函數時,$api實際上是一個不同的變量。它在源代碼中有相同的名稱,但它不代表相同的內存。

現在,PHP不知道當你創建$api2時,你不會再使用$api1,所以它必須保存在內存中;隨着越來越多的數據集結束,它需要越來越多的內存。

user3720435的建議是遞歸之前添加unset($api)

$api = GetConectApi ($index, offset)-> Getrecords(); 
foreach ($api => $value) { 
     // Here is my necessary loop 
} 
if (count ($api) > 0) { 
     unset($api); 
     // code as before 
} 

這將告訴PHP,你不需要內存了,所以它遞歸,也不會建立。您仍然會構建$index$offset的多個副本,但相比之下,這些可能非常小。

所有的說法,目前尚不清楚爲什麼你需要在這裏遞歸。整個事情實際上可以改變一個簡單的循環:

do { 
    $api = GetConectApi ($index, offset)-> Getrecords(); 
    foreach ($api => $value1) { 
     // Here is my necessary loop 
    } 
    $index = $index + $offset; 
} while (count ($api) > 0) 

do..while循環總是執行一次,然後不斷重複,直到條件爲假。打開它看起來是這樣的:

// do... 
    $api = GetConectApi ($index, offset)-> Getrecords(); 
    foreach ($api => $value1) { 
     // Here is my necessary loop 
    } 
    $index = $index + $offset; 
if (count ($api) > 0) { // while... 
$api = GetConectApi ($index, offset)-> Getrecords(); 
    foreach ($api => $value1) { 
     // Here is my necessary loop 
    } 
    $index = $index + $offset; 
} 
if (count ($api) > 0) { // while... 
// etc 

請注意,我們並不需要,因爲我們去一輪循環分配任何額外的內存,因爲我們還沒有進入一個新的功能 - 我們只是使用相同的變量一遍又一遍地。

+0

非常感謝,您的解釋幫助了我很多,並解決了我的問題,感謝真正的 –

2

您可以嘗試使用$ api變量進行清理。

$cnt = count($api); 
$api = null; 
unset($api); 
if ($cnt > 0) { 
+0

但是,如果我給我的API未設置,我不會再次創建該變量?這不會產生相同的內存消耗嗎? –

+1

您每次調用遞歸時都會創建一個新的$ api。這並沒有改變。 嘗試一下: $ cnt = count($ api); $ api = null; 未設置($ api); if($ cnt> 0){ – user3720435

1

您可以使用隊列系統獲取所有數據並將其保存到你的數據庫像RMQ

,或者您可以[指數]在你的數據庫假設爲0

然後你加cron作業不遞歸獲取來自API的數據,並會每分鐘運行一次例如

將進入數據庫獲取指數和你的偏移量和獲取數據,並增加索引

1分鐘後,工作將再次運行到數據庫獲取指數和你的偏移量和獲取數據,並增加索引等

+0

非常感謝您的回答,上面的答案幫助我 –