php 執行mysql中查詢時內存溢出怎麼辦?
phpthink框架寫的小網站,原來用5萬條數據測試時一切正常。數據量加到25萬時一直報錯內存溢出。請問該怎麼改。
在回答問題之前,先對題主的提問方式提出一點小建議,問這種TroubleShooting相關的問題,除了貼上錯誤信息外,最好附帶貼上你的相關程序代碼,沒貼代碼,大家只能靠假設,正如此時我就假設題主的問題如我以下所言^_^
儘管PHP的語言特性使得大多數情況下開發者不需要太關注內存的使用,但是題主的這個問題確實
是很多新手容易犯的錯誤簡單來說,在處理數據的過程中,不要一次性讀出所有的數據,容易造成不必要的開銷以致於內存溢出在大多數情況下,如魯小夫童鞋所說,在Web開發過程中,大多數情況下,這種遍歷大數據的情況都是可以避免的,而如果確實需要取出這麼大的結果集,有以下幾個可參考的優化方案1、寫SQL語句的時候,盡量不使用SELECT * ,而是指定需要的column,這樣對資料庫和對PHP都有好處,同時能夠減少網路IO
2、不要一次性從資料庫中讀出所有數據,例如使用原生PDO時,不要使用$db-&>query($sql)-&>fetchAll(); 方法,題主使用的ThinkPHP框架中,不要使用ORM模型的select()方法這是多數對PHP或者說對Web開發不熟悉的初學者容易犯的錯誤,實質上在上面提到的fetchAll()和select()方法內部,都是已經完整,注意是完整的遍歷過一遍結果集,將從資料庫取出來的所有數據都遍歷了一遍整合成二維數組後作為函數返回值返回的,這在很多情況下可以為開發者提供方便的調用,但是在數據量大的時候,即便內存沒有溢出,也至少多產生了一次不必要的循環。正確的做法是,原生PDO使用$res = $db-&>query($sql)-&>fetch()方法,返回的是一個游標,可以通過循環該游標遍歷結果集$stmt = $db-&>query($sql);
if($stmt){
while($res = $stmt-&>fetch()){
//循環中對每行數據進行讀取和操作
}
}
ThinkPHP框架沒怎麼用過,但是應該也有提供返回遊標的方法,可以參考一下文檔
大多數時候遍歷大數據,都不是為了將這些數據收集到一個數組裡,而是為了進行某些統計或者輸出(到頁面或者到文本),通過游標訪問的方式,可以避免將所有結果集都放在一個數組裡導致內存溢出這種情況(一種是循環只產生一條數據的內存佔用,隨後被下次循環的變數覆蓋,另一種是25w倍的內存佔用,結果可想而知)3、但是某些情況下,又確實需要將所有數據放到一個數組裡,但正如開頭及魯小夫所說,對於Web而言,這種大數據量必然不可能一次性展示在頁面上,所以通過分頁輸出即可
4、再再某些特殊情況下,又不能分頁,又必須一次性讀出所有數據放到內存,那就設置ini_set(memory_limit, 0); 吧,但是這種頁面不可能出現在Web頁面上供用戶瀏覽(即便內存處理的過來,可能程序的時間也會超時導致504,即便程序不超時,產生的頁面內容也是巨大的,不可能作為一個前台展示給用戶的頁面),所以只能是在命令行方式下,跑腳本使用。如果你的 web 應用,用 PHP 從資料庫中 select 出一個 25 萬行的結果集,那麼你多半是做錯了。因為我不覺得在任何情況下,有必要在一個 HTTP 響應中使用這麼大的結果集。
當然了,在使用到超大的結果集時,PHP 也是可以勝任的,因為 PDO 中有 cursor 的概念,數據可以一行一行取出來,不會一下子佔用很多內存。使用 unbuffered cursor 可以避免內存問題。
cursor 默認使用 buffered 模式。這種模式會把所有結果集返回並載入內存。如果結果集很大的話,內存會爆。
unbuffered cursor 是每次只將下一行結果返回,內存佔用很小。不過這種模式缺點很多,所以沒有作為默認的模式。作為一個菜鳥 ....怎麼說呢 你取出那麼多數據 放哪裡能都顯示呢? 顯示出來 誰能看完? 所以要分頁啊 騷年
游標那個有點搞笑
你limit一下會死嗎?
你把thinkphp寫成phpthink。完了犯這麼低級的錯誤。我要是 @劉晨,估計這會兒被氣死了
請limit下。。。一次取幾十萬數據是作死。即使你是做一個簡單的,做著玩的網站也要注意下執行時間,數據傳輸量。看你這controller的命名,是首頁?除非是執行些管理頁面的需求,否則根本就不應該讀這麼多數據出來。
推薦閱讀:
※PHP 7 新增的生成器特性
※有哪些好的wordpress中文主題原創作者?
※仿照TP3.2製作MVC框架(一) 文檔簡介
※PHP初學者應該選擇哪個版本的ThinkPHP?
※PHP易錯面試題收集-持續更新