PHP查詢MySQL大量數據的內存占用分析
本文主要從原理、手冊和源碼分析PHP查詢MySQL時返回大量結果的內存占用問題,同時也涉及到MySQL C API的使用。
昨天有同事在PHP討論組提到,他在做的一個項目,MySQL查詢返回的結果太多(最多10萬條),導致PHP內存不足。于是,他問,執行完下面的代碼遍歷返回MySQL結果之前,數據是否已經在內存中了? -
while($row=mysql_fetch_assoc($result)){
//.
}
當然,針對這類問題的優化方法有很多。不過,就這個問題,我首先想到的是MySQL是經典的C/S(Client/Server,客戶端/服務器)模型。在遍歷結果集之前,底層實現可能已經通過網絡(假設使用TCP/IP)將所有數據讀到客戶端緩沖區,還有一種可能是數據還在服務端的發送緩沖區中,并且已經沒有傳送給客戶端。
之前看PHP和MySQL的源碼,注意到PHP手冊:中有兩個功能相似的函數
mysql_查詢()
mysql_unbuffered_query()
這兩個函數的字面意思和描述證實了我的想法。執行前一個函數時,會將服務器端的所有結果集讀取到客戶端緩沖區,而后一個函數則不會。這就是“無緩沖(unbuffered)”的意思。
也就是說,如果使用mysql_unbuffered_query()執行一個返回大結果集的SQL語句,在遍歷結果之前,PHP的內存是不會被結果集占用的。如果用mysql_query()執行同樣的語句,函數返回,PHP的內存占用會急劇增加,內存會馬上被耗盡。
如果你看過PHP的相關代碼,就可以看出這兩個函數實現的異同:
/*{{{protoresourcemysql_query(stringquery[,intlink_identifier])
向MySQL 發送SQL 查詢*/
PHP_FUNCTION(mysql_query)
{
php_mysql_do_query(INTERNAL_FUNCTION_PARAM_PASSTHRU,MYSQL_STORE_RESULT);
}
/*}}}*/
/*{{{protoresourcemysql_unbuffered_query(stringquery[,intlink_identifier])
向MySQL 發送SQL 查詢,不獲取和緩沖結果行*/
PHP_FUNCTION(mysql_unbuffered_query)
{
php_mysql_do_query(INTERNAL_FUNCTION_PARAM_PASSTHRU,MYSQL_USE_RESULT);
}
/*}}}*/
兩個函數都調用了php_mysql_do_query(),只是第二個參數不同,MYSQL_STORE_RESULT和MYSQL_USE_RESULT。查看php_mysql_do_query()的實現:
如果(使用存儲==MYSQL_USE_RESULT){
mysql_result=mysql_use_result(mysql-conn);
}別的{
mysql_result=mysql_store_result(mysql-conn);
}
mysql_use_result() 和mysql_store_result() 是MySQL C API 函數。這兩個C API函數的區別在于,后者是從MySQL Server讀取所有的結果集到Client,而前者只讀取結果集的元信息。
回到PHP,使用mysql_unbuffered_query() 來避免直接內存占用。如果在遍歷過程中結果沒有被“PHP緩存”(比如放在一個數組中),雖然整個執行過程操作了10萬條或者百萬條或者更多的數據,但是PHP占用的內存總是很小的。
標簽: 北京網站制作高端網站建設
我們專注高端建站,小程序開發、軟件系統定制開發、BUG修復、物聯網開發、各類API接口對接開發等。十余年開發經驗,每一個項目承諾做到滿意為止,多一次對比,一定讓您多一份收獲!