PHP查詢MySQL大量數(shù)據(jù)的時候內(nèi)存占用分析
更新時間:2011年07月22日 23:18:34 作者:
這篇文章主要是從原理, 手冊和源碼分析在PHP中查詢MySQL返回大量結(jié)果時, 內(nèi)存占用的問題, 同時對使用MySQL C API也有涉及.
昨天, 有同事在PHP討論群里提到, 他做的一個項(xiàng)目由于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é)果集之前, 底層的實(shí)現(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í)了我的想法, 前一個函數(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ù)的實(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);
}
/* }}} */
/* {{{ 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()的實(shí)現(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ù)組中), 則整個執(zhí)行過程雖然操作了十萬條或者百萬條或者更多的數(shù)據(jù), 但PHP占用的內(nèi)存始終是非常小的.
復(fù)制代碼 代碼如下:
while ($row = mysql_fetch_assoc($result)) {
// ...
}
當(dāng)然, 這種問題有許多優(yōu)化的方法. 不過, 就這個問題來講, 我首先想到, MySQL是經(jīng)典的C/S(Client/Server, 客戶端/服務(wù)器)模型, 在遍歷結(jié)果集之前, 底層的實(shí)現(xiàn)可能已經(jīng)把所有的數(shù)據(jù)通過網(wǎng)絡(luò)(假設(shè)使用TCP/IP)讀到了Client的緩沖區(qū), 也有另一種可能, 就是數(shù)據(jù)還在Server端的發(fā)送緩沖區(qū)里, 并沒有傳給Client.
在查看PHP和MySQL的源碼之前, 我注意到PHP手冊里有兩個功能相近的函數(shù):
復(fù)制代碼 代碼如下:
mysql_query()
mysql_unbuffered_query()
兩個函數(shù)的字面意思和說明證實(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ù)的實(shí)現(xiàn)上的異同:
復(fù)制代碼 代碼如下:
/* {{{ 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);
}
/* }}} */
/* {{{ 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()的實(shí)現(xiàn):
復(fù)制代碼 代碼如下:
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ù)組中), 則整個執(zhí)行過程雖然操作了十萬條或者百萬條或者更多的數(shù)據(jù), 但PHP占用的內(nèi)存始終是非常小的.
您可能感興趣的文章:
- 解決PHP里大量數(shù)據(jù)循環(huán)時內(nèi)存耗盡的方法
- php post大量數(shù)據(jù)時發(fā)現(xiàn)數(shù)據(jù)丟失問題解決方法
- php查詢mysql大量數(shù)據(jù)造成內(nèi)存不足的解決方法
- php導(dǎo)入大量數(shù)據(jù)到mysql性能優(yōu)化技巧
- 2個自定義的PHP in_array 函數(shù),解決大量數(shù)據(jù)判斷in_array的效率問題
- php 表單提交大量數(shù)據(jù)發(fā)生丟失的解決方法
- phpExcel導(dǎo)出大量數(shù)據(jù)出現(xiàn)內(nèi)存溢出錯誤的解決方法
- phpmyadmin里面導(dǎo)入sql語句格式的大量數(shù)據(jù)的方法
- asp和php下textarea提交大量數(shù)據(jù)發(fā)生丟失的解決方法
- PHP查詢大量數(shù)據(jù)內(nèi)存耗盡問題的解決方法
相關(guān)文章
關(guān)于使用key/value數(shù)據(jù)庫redis和TTSERVER的心得體會
本篇文章是對使用key/value數(shù)據(jù)庫redis和TTSERVER的心得體會進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
yii2項(xiàng)目實(shí)戰(zhàn)之restful api授權(quán)驗(yàn)證詳解
這篇文章主要給大家介紹了關(guān)于yii2項(xiàng)目實(shí)戰(zhàn)之restful api授權(quán)驗(yàn)證的相關(guān)資料,文中介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2017-05-05
關(guān)于php 高并發(fā)解決的一點(diǎn)思路
涉及搶購、秒殺、抽獎、搶票等活動時,為了避免超賣,那么庫存數(shù)量是有限的,但是如果同時下單人數(shù)超過了庫存數(shù)量,就會導(dǎo)致商品超賣問題。那么我們怎么來解決這個問題呢,我的思路如下2017-04-04
PHP mysqli_free_result()與mysqli_fetch_array()函數(shù)詳解
這篇文章主要介紹了PHP mysqli_free_result()與mysqli_fetch_array()函數(shù)詳解的相關(guān)資料,需要的朋友可以參考下2016-09-09
PHP 與 UTF-8 的最佳實(shí)踐詳細(xì)介紹
這篇文章主要介紹了PHP 與 UTF-8 的最佳實(shí)踐詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-01-01
php array_filter除去數(shù)組中的空字符元素
php array_filter除去數(shù)組中的空字符元素,array_filter() 函數(shù)根據(jù)回調(diào)函數(shù)過濾數(shù)組中的值,省略回調(diào)函數(shù)則默認(rèn)過濾空值,需要的朋友可以參考下。2011-11-11
php curl獲取https頁面內(nèi)容,不直接輸出返回結(jié)果的設(shè)置方法
今天小編就為大家分享一篇php curl獲取https頁面內(nèi)容,不直接輸出返回結(jié)果的設(shè)置方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01
PHP基于curl模擬post提交json數(shù)據(jù)示例
這篇文章主要介紹了PHP基于curl模擬post提交json數(shù)據(jù)操作,結(jié)合實(shí)例形式分析了php使用curl實(shí)現(xiàn)post方式提交json數(shù)據(jù)相關(guān)操作步驟與注意事項(xiàng),代碼簡單實(shí)用,需要的朋友可以參考下2018-06-06

