2011-10-24 73 views
1

我使用PHP 5.3.6與PDO來訪問Postgres 9.0.4。我被要求減少報告的內存佔用量。當前的實現很簡單:執行查詢,執行fetchAll(),然後通過結果數組遍歷foreach()。這顯然不會隨着巨大的結果集擴展:它可以暫時消耗100MB或更多。迭代Postgres/PHP/PDO中結果集的最佳實踐?

我有一個新的實現,它採用PDO語句句柄,然後使用foreach()直接迭代它,即通過fetchAll()沒有中間數組。 (從我讀過的內容中,迭代一個語句句柄,並在foreach中調用fetch())。這同樣快,並且消耗的方式是更少的內存:大約28kB。不過,我不相信我這樣做是正確的,因爲雖然我已經做了谷歌搜索噸的,這是很難找到答案這個基本問題:

  • 我見過的文章,建議用遊標解決我原來的問題。 Postgress PDO驅動程序是否已經在內部使用遊標?如果需要編寫自己的SQL來創建遊標,我願意,但我寧願寫最簡單的代碼(但不是簡單的!)。

  • 如果foreach調用fetch()每次迭代,是不是太網絡健談?或者它很聰明並且一次取多行,例如500,節省帶寬? (這可能意味着它在內部使用光標。)

  • 我見過一篇文章,將語句句柄包裝在實現Iterator接口的類中。鑑於PDO語句句柄已經做到這一點,這不是多餘的嗎?或者我錯過了什麼?

  • 我的電話準備SQL語句如下:

    而$ sth = $ dbh->準備($的SQL);

我發現,它並沒有內存或速度的區別,如果我這樣做:

$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)); 

這是因爲這是默認反正Postgres的PDO驅動程序?如果它已經在內部使用光標,這將是有意義的。

歡迎提供有關解決此問題的方法和其他方法的一般意見。

回答

1

PDO for Postgres does use cursors internally

+0

謝謝,喬恩。確實如此 - 我沒有想過要直視源頭。我讀到它說,沒有智能一次提取多於一行,並緩衝它們在客戶端以減少網絡流量。除非發生在Postgres客戶端的更深層次,否則驅動程序會調用它,但似乎不太可能。我會做一些性能測試,看看我的簡單實現如何在不同的盒子上與客戶端和Postgres一起工作。如果它太慢,我會推出自己的。 – DaveBurns

+0

FWIW,我認爲驅動程序源代碼是它沒有使用prepare()的可選參數。希望聽到有人不同意。 – DaveBurns

0

顯然PDO::CURSOR_FWDONLY不使用遊標。黑盒子測試:

(0)準備:

$con = new \PDO('dsn'); 
// you'll get "NO ACTIVE TRANSACTION" otherwise 
$con->beginTransaction(); 

$sql = 'select * from largetable'; 

(1)默認 - 需要永遠:

$stmt = $con->prepare($sql); 
$stmt->execute(); 
print_r($stmt->fetch()); 

(2)FWDONLY - 永遠需要:

$stmt = $con->prepare($sql, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY)); 
$stmt->execute(); 
print_r($stmt->fetch()); 

(3)SCROLLABLE - 在閃存中運行:

$stmt = $con->prepare($sql, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL)); 
$stmt->execute(); 
print_r($stmt->fetch()); 

我打開PG日誌記錄只是爲了確定,它確實如此 - 只有SCROLL使用遊標。

所以,使用遊標的唯一方法是使用SCROLL,至少在PHP 5.4.23中。