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