很多朋友都可能碰到幾萬數(shù)據(jù)查詢mysql就會占很高的內(nèi)存和cup特別是在windows系統(tǒng)下,下面我們來看看原因分析吧.
有同事在PHP討論群里提到,他做的一個項目由于MySQL查詢返回的結(jié)果太多(達(dá)10萬條), 從而導(dǎo)致PHP內(nèi)存不夠用.所以,他問,在執(zhí)行下面的代碼遍歷返回的MySQL結(jié)果之前, 數(shù)據(jù)是否已經(jīng)在內(nèi)存中了? 代碼如下:
while ($row = mysql_fetch_assoc($result)) { // ... }
當(dāng)然, 這種問題有許多優(yōu)化的方法,不過,就這個問題來講,我首先想到,MySQL是經(jīng)典的C/S(Client/Server, 客戶端/服務(wù)器)模型, 在遍歷結(jié)果集之前,底層的實現(xiàn)可能已經(jīng)把所有的數(shù)據(jù)通過網(wǎng)絡(luò)(假設(shè)使用TCP/IP)讀到了Client的緩沖區(qū),也有另一種可能,就是數(shù)據(jù)還在Server端的發(fā)送緩沖區(qū)里,并沒有傳給Client.
在查看PHP和MySQL的源碼之前,我注意到PHP手冊里有兩個功能相近的函數(shù),代碼如下:
mysql_query() mysql_unbuffered_query()
兩個函數(shù)的字面意思和說明證實了我的想法,前一個函數(shù)執(zhí)行時,會把所有的結(jié)果集從Server端讀到Client端的緩沖區(qū)中,而后一個則沒有,這就是”unbuffered(未緩沖)”的意思.
那就是說,如果用mysql_unbuffered_query()執(zhí)行了一條返回大量結(jié)果集的SQL語句,在遍歷結(jié)果之前,PHP的內(nèi)存是沒有被結(jié)果集占用的,而用mysql_query()來執(zhí)行同樣的語句的話,函數(shù)返回時,PHP的內(nèi)存占用便會急劇增加,立即耗光內(nèi)存.
如果閱讀PHP的相關(guān)代碼,可以看到這兩個函數(shù)的實現(xiàn)上的異同,代碼如下:
- /* {{{ proto resource mysql_query(string query [, int link_identifier]) Sends an SQL query to MySQL */ PHP_FUNCTION(mysql_query) {
- php_mysql_do_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQL_STORE_RESULT); }
- --Vevb.com
- /* }}} */
- /* {{{ proto resource mysql_unbuffered_query(string query [, int link_identifier]) Sends an SQL query to MySQL, without fetching and buffering the result rows */ PHP_FUNCTION(mysql_unbuffered_query) { php_mysql_do_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQL_USE_RESULT); } /* }}} */
兩個函數(shù)都調(diào)用了php_mysql_do_query(),只差了第2個參數(shù)的不同,MYSQL_STORE_RESULT和MYSQL_USE_RESULT.再看php_mysql_do_query()的實現(xiàn):
- if(use_store == MYSQL_USE_RESULT) {
- mysql_result=mysql_use_result(&mysql->conn);
- } else {
- mysql_result=mysql_store_result(&mysql->conn);
- }
mysql_use_result()和mysql_store_result()是MySQL的C API函數(shù),這兩個C API函數(shù)的區(qū)別就是后者把結(jié)果集從MySQL Server端全部讀取到了Client端,前者只是讀取了結(jié)果集的元信息.
回到PHP,使用mysql_unbuffered_query(),可以避免內(nèi)存的立即占用,如果在遍歷的過程不對結(jié)果進(jìn)行”PHP緩存”(如放到某數(shù)組中),則整.
新聞熱點
疑難解答
圖片精選