dietoad给出如下改进: -
-
-
- public function row ($n = 0, $type = 'array')
- {
- if(isset($this->_rowData[$n]))
- {
- return $this->_rowData[$n];
- }
- if (! is_numeric($n))
- {
- return $this->rowObject($n);
- }
-
- $ln=count($this->_rowData);
-
- while($ln++<=$n&&$r=$this->_fetchAssoc())
- {
- $this->_rowData[]=$r;
- }
-
-
- return isset($this->_rowData[$n])?$this->_rowData[$n]:array();
- }
在今年的4月末,鄙人写过另一篇关于CodeIgniter框架的设计缺陷问题,给我们游戏项目带来较大的影响,后来提交到github issues,并没得到回复,想了想,虽然官方的2.1.3版本中,也存在这个小问题。不过我觉得,这就不提交了,或许,我们的做法也符合他们的设计初衷。不过,我们还是在我们的项目中改进了。 如此改进之后,我们使用php的memory_get_usage()函数观察前后两个row()方法的结果时,果然发现内存使用情况有较大改善(改善幅度取决于SELECT的返回数据量)。 似乎,到这里就应该结束了,问题就这么被发现,被解决了。 但,我总觉得少了些什么呢?当我再次strace抓包时,发现仍然存在大量的数据通讯,就像文章开头的那副截图一模一样。然而,这又是什么原因呢? 我顺手写了个内存占用的测试代码如下: - $db = mysql_connect('192.168.xx.xx','xxxx','xxxx');
- $sql = 'SELECT * from items';
- mysql_select_db('jv01',$db);
- echo 'SELECT_DB: ',convert(memory_get_usage()),"\n";
-
- $r = mysql_query($sql,$db);
- echo 'QUERY_SQL: ',convert(memory_get_usage()),"\n";
-
-
- $arr = array();
- while ($rs = mysql_fetch_assoc($r))
- {
- $arr[]=$rs;
- }
- echo 'FETCH_RS: ',convert(memory_get_usage()),"\n";
-
- unset($arr);
- echo 'UNSET: ',convert(memory_get_usage()),"\n";
-
- mysql_free_result($r);
- echo 'FREE_R: ',convert(memory_get_usage()),"\n";
-
-
-
- function convert($size)
- {
- $unit=array('b','kb','mb','gb','tb','pb');
- return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
- }
-
-
-
-
-
-
-
-
看到结果时,我不禁XX一紧,什么?这你妈什么情况?查询完之后,内存大小居然只增加了不到1k?我那个表可是几十M的数据啊?遍历结果集之后,怎么突增几十M啊?尼玛这到底是什么情况?strace返回的大量数据到底存在哪的?算不算php进程申请的? 后来,我再次执行如上程序,再定时用free、/proc/PID/maps 之类系统工具,查看系统的内存使用情况,确认了当前进程的内存占用确实存在。那么可能的情况就是memory_get_usage()函数并没有获取到 mysql_query之后的内存占用情况。由于比较怀疑,末学跟进了memory_get_usage()函数的源码,该函数直接交给 zend_memory_usage函数处理。 -
- ZEND_API size_t zend_memory_usage(int real_usage TSRMLS_DC)
- {
- if (real_usage) {
- return AG(mm_heap)->real_size;
- } else {
- size_t usage = AG(mm_heap)->size;
- #if ZEND_MM_CACHE
- usage -= AG(mm_heap)->cached;
- #endif
- return usage;
- }
- }
-
-
-
-
-
-
- ZEND_API void *_emalloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
- {
- TSRMLS_FETCH();
-
- if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) {
- return AG(mm_heap)->_malloc(size);
- }
- return _zend_mm_alloc_int(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
- }
php的内存管理 (中文地址:php-zend的内存管理中文版)这块,对于末学来说,太复杂了,只是稍微看懂直接 返回了mm_heap结构体的real_size/size的值。(两篇都是鸟哥写的,中文的地址也就是鸟哥博客最近一直打不开,抽风得厉害) 那mysql_query的结果集,存在哪的呢?如何申请内存的,莫非不是调用zend的_emalloc内存分配函数的?这得先明确mysql客户端类库问题,也就是我们使用哪个类库?libmysql还是mysqlnd,通过查看编译参数,发现(我的虚拟机)是libmysql,编译参数是这样的 - ./configure' '--prefix=/services/php_5.3.19' '--with-config-file-path=/services/php_5.3.19/etc' '--with-pdo-mysql=/usr/bin/mysql_config' '--with-mysql=/usr/bin/mysql_config' '--with-mysqli=/usr/bin/mysql_config' '--enable-bcmath' '--enable-fpm
-
-
- ./configure' '--prefix=/services/php' '--with-config-file-path=/services/php/etc' '--with-pdo-mysql=mysqlnd' '--with-mysql=mysqlnd' '--with-mysqli=mysqlnd' '--enable-bcmath' '--enable-fpm
有点乱: mysql、mysqli、pdo-mysql、libmysql、mysqlnd 好多名词,有点乱,没关系,一张图让你清晰起来: 
mysql、mysqli、pdo-mysql、libmysql、mysqlnd之间关系 mysqlnd跟libmysql一样,都是直接与mysql server通讯的驱动类库。 而php程序员使用的mysql、mysqli、pdo-mysql是面向程序员调用的API接口。 |