PHP查询MySQL大量数据的时候内存占用分析


Posted in PHP onJuly 22, 2011

昨天, 有同事在PHP讨论群里提到, 他做的一个项目由于MySQL查询返回的结果太多(达10万条), 从而导致PHP内存不够用. 所以, 他问, 在执行下面的代码遍历返回的MySQL结果之前, 数据是否已经在内存中了? -

while ($row = mysql_fetch_assoc($result)) { 

// ... 

}

当然, 这种问题有许多优化的方法. 不过, 就这个问题来讲, 我首先想到, MySQL是经典的C/S(Client/Server, 客户端/服务器)模型, 在遍历结果集之前, 底层的实现可能已经把所有的数据通过网络(假设使用TCP/IP)读到了Client的缓冲区, 也有另一种可能, 就是数据还在Server端的发送缓冲区里, 并没有传给Client.

在查看PHP和MySQL的源码之前, 我注意到PHP手册里有两个功能相近的函数:

mysql_query() 

mysql_unbuffered_query()

两个函数的字面意思和说明证实了我的想法, 前一个函数执行时, 会把所有的结果集从Server端读到Client端的缓冲区中, 而后一个则没有, 这就是”unbuffered(未缓冲)”的意思.

那就是说, 如果用mysql_unbuffered_query()执行了一条返回大量结果集的SQL语句, 在遍历结果之前, PHP的内存是没有被结果集占用的. 而用mysql_query()来执行同样的语句的话, 函数返回时, PHP的内存占用便会急剧增加, 立即耗光内存.

如果阅读PHP的相关代码, 可以看到这两个函数的实现上的异同:

/* {{{ 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); 

} 

/* }}} */

两个函数都调用了php_mysql_do_query(), 只差了第2个参数的不同, MYSQL_STORE_RESULT和MYSQL_USE_RESULT. 再看php_mysql_do_query()的实现:

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函数, 这两个C API函数的区别就是后者把结果集从MySQL Server端全部读取到了Client端, 前者只是读取了结果集的元信息.

回到PHP, 使用mysql_unbuffered_query(), 可以避免内存的立即占用. 如果在遍历的过程不对结果进行”PHP缓存”(如放到某数组中), 则整个执行过程虽然操作了十万条或者百万条或者更多的数据, 但PHP占用的内存始终是非常小的.

PHP 相关文章推荐
PHP file_get_contents 函数超时的几种解决方法
Jul 30 PHP
setcookie中Cannot modify header information-headers already sent by错误的解决方法详解
May 08 PHP
php 生成自动创建文件夹并上传文件的示例代码
Mar 07 PHP
PHP的Yii框架中创建视图和渲染视图的方法详解
Mar 29 PHP
ThinkPHP简单使用memcache缓存的方法
Nov 15 PHP
redis查看连接数及php模拟并发创建redis连接的方法
Dec 15 PHP
PHP递归实现文件夹的复制、删除、查看大小操作示例
Aug 11 PHP
微信公众号开发之获取位置信息php代码
Jun 13 PHP
PHP解析url并得到url参数方法总结
Oct 11 PHP
php实现获取近几日、月时间示例
Jul 06 PHP
php使用curl伪造浏览器访问操作示例
Sep 30 PHP
基于Laravel(5.4版本)的基本增删改查操作方法
Oct 11 PHP
PHP性能优化 产生高度优化代码
Jul 22 #PHP
PHP多个版本的分析解释
Jul 21 #PHP
QQ登录 PHP OAuth示例代码
Jul 20 #PHP
模板引擎正则表达式调试小技巧
Jul 20 #PHP
php中批量替换文件名的实现代码
Jul 20 #PHP
关于php连接mssql:pdo odbc sql server
Jul 20 #PHP
PHP mcrypt可逆加密算法分析
Jul 19 #PHP
You might like
第五章 php数组操作
2011/12/30 PHP
php页面消耗内存过大的处理办法
2013/03/18 PHP
JS日历 推荐
2006/12/03 Javascript
window.open被浏览器拦截后的自定义提示效果代码
2007/11/19 Javascript
摘自启点的main.js
2008/04/20 Javascript
JavaScript中for..in循环陷阱介绍
2013/11/12 Javascript
Jquery实现的一种常用高亮效果示例代码
2014/01/28 Javascript
JS循环遍历JSON数据的方法
2014/07/08 Javascript
JSONP之我见
2015/03/24 Javascript
jquery.validate提示错误信息位置方法
2016/01/22 Javascript
Vue.js实现无限加载与分页功能开发
2016/11/03 Javascript
详解JS中的立即执行函数
2017/02/24 Javascript
微信小程序封装http访问网络库实例代码
2017/05/24 Javascript
angular select 默认值设置方法
2017/06/23 Javascript
vue-awesome-swiper滑块插件使用方法详解
2017/11/27 Javascript
快速搭建Node.js(Express)用户注册、登录以及授权的方法
2019/05/09 Javascript
Vue使用screenfull实现全屏效果
2020/09/17 Javascript
[03:10]2014DOTA2 TI马来劲旅Titan首战告捷目标只是8强
2014/07/10 DOTA
[01:38]DOTA2 2015国际邀请赛中国区预选赛 Showopen
2015/06/01 DOTA
Python中用Descriptor实现类级属性(Property)详解
2014/09/18 Python
Django框架中方法的访问和查找
2015/07/15 Python
python迭代dict的key和value的方法
2018/07/06 Python
Numpy截取指定范围内的数据方法
2018/11/14 Python
基于Python共轭梯度法与最速下降法之间的对比
2020/04/02 Python
keras的siamese(孪生网络)实现案例
2020/06/12 Python
python 字符串的驻留机制及优缺点
2020/06/19 Python
英国标志性奢侈品牌:Burberry
2016/07/28 全球购物
阿迪达斯丹麦官网:adidas丹麦
2016/10/01 全球购物
实习生个人的自我评价
2013/12/08 职场文书
司法所长先进事迹
2014/06/02 职场文书
文明工地标语
2014/06/16 职场文书
退休欢送会致辞
2015/07/31 职场文书
毕业生自我鉴定范文
2019/05/13 职场文书
SQL 窗口函数实现高效分页查询的案例分析
2021/05/21 SQL Server
redis实现的四种常见限流策略
2021/06/18 Redis
SpringBoot项目多数据源及mybatis 驼峰失效的问题解决方法
2022/07/07 Java/Android