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接口对接开发等。十余年开发经验,每一个项目承诺做到满意为止,多一次对比,一定让您多一份收获!