Doctrine 查询内存使用量

10

Doctrine似乎在执行单个简单查询时占用了超过4MB的RAM:

print memory_get_peak_usage()." <br>\n";
$q = Doctrine_Query::create()
    ->from('Directories d')
    ->where('d.DIRECTORY_ID = ?', 5);

$dir = $q->fetchOne();
print $dir['name']." ".$dir['description']."<br>\n";

print memory_get_peak_usage()." <br>\n";

/***************  OUTPUT:  **************************

6393616
testname testdescription
10999648

/***************************************************/

这是一个测试数据库,并且里面的数据很少 - 我查询的项目除了显示在此处的内容以外不包含任何其他数据。

我的系统设置可能存在问题,还是Doctrine的标准内存使用情况?

7个回答

6

从我所看到的,你的代码似乎没有问题...


作为测试,我设置了一个快速示例,只有一个非常简单的表格(仅四个字段)

这是相关的代码:

var_dump(number_format(memory_get_peak_usage()));

$test = Doctrine::getTable('Test')->find(1);

var_dump(number_format(memory_get_peak_usage()));

在执行此操作时,我得到了这种输出:
string '1,316,088' (length=9)
string '2,148,760' (length=9)

考虑到表格非常简单,我只获取了一行数据,对我来说似乎有点“过度”——但这与您得到的结果以及我在其他项目中看到的情况非常一致 :-(
如果您只需要显示数据而不需要处理它(例如更新/删除等),解决方案可能是不获取复杂对象,而只获取一个简单的数组:
$test = Doctrine::getTable('Test')->find(1, Doctrine::HYDRATE_ARRAY);

但是在这种情况下,实际上并没有太大的区别 :-( :
string '1,316,424' (length=9)
string '2,107,128' (length=9)

只有40 KB的差异 - 嗯,对于更大的对象/更多的行,这可能仍然是一个好主意...


在Doctrine手册中,有一个名为Improving Performance的页面;也许它可以帮助你,特别是对于这些部分:


哦,顺便说一下:我在PHP 5.3.0上进行了这个测试;也许这会对使用的内存量产生影响...


这让我担心,因为我正在将Doctrine集成到我的框架中。 - Steven Mercatante
在过度担忧之前,您可能需要进行更多的测试,使用更大的表格、更多的数据等等,以查看内存增加是否呈线性增长。 另外注意:我曾经在基于Zend Framework和Symfony的项目中看到Doctrine的使用,这从未成为问题... - Pascal MARTIN
Pascal MARTIN:也许你知道的这些网站负载不重?我想知道是否有一些主流网站正在使用Doctrine。 - Fragsworth
1
通常我会将新项目锁定到16MB的内存限制。除了一次例外,我从未用完Doctrine的内存限制。此外,当Doctrine首次使用时,会启动大量相互引用的变量和单例模式。 - David
1.2 版本的文档链接现在可以在 http://readthedocs.org/docs/doctrine/en/latest/en/manual/improving-performance.html 找到。 - Manu

5

我同意romanb的答案 - 当使用大型库/框架时,使用OpCode缓存绝对是必须的。

与OpCode缓存相关的例子

我最近开始在Zend Framework中使用Doctrine,并对内存使用情况产生了好奇 - 所以像题主一样,我创建了一个使用类似标准的方法,通过运行整体测试来观察ZF + Doctrine的峰值内存使用情况。

我得到了以下结果:

没有使用APC的结果:

10.25 megabytes
RV David
16.5 megabytes

使用APC的结果:

3 megabytes
RV David
4.25 megabytes

操作码缓存会带来非常显著的差别。


4

使用Doctrine Query的fetchOne()函数需要注意。这个函数不会在SQL语句中附加"Limit 1"语句。

如果您只需要从数据库中获取一条记录,请确保:

$q->limit(1)->fetchOne() 

大型表的内存使用量大幅下降。

您可以看到,fetchOne()首先将从数据库中获取一个集合,然后返回第一个元素。

public function fetchOne($params = array(), $hydrationMode = null)
{
    $collection = $this->execute($params, $hydrationMode);

    if (is_scalar($collection)) {
        return $collection;
    }

    if (count($collection) === 0) {
        return false;
    }

    if ($collection instanceof Doctrine_Collection) {
        return $collection->getFirst();
    } else if (is_array($collection)) {
        return array_shift($collection);
    }

    return false;
}

4

那么这个内存使用量是从哪里来的呢?正如Pascal MARTIN所指出的,数组水合并没有太大的区别,这是合理的,因为我们只谈论了一些记录。

内存消耗来自于通过自动加载按需加载的所有类。

如果您没有设置APC,则系统设置有问题。不要试图测量性能并期望在没有像APC这样的opcode缓存的情况下获得良好的结果。它不仅可以加速执行,还可以在除第一个页面加载(其中APC需要首先缓存字节码)之外的所有页面加载中将内存使用量减少至少50%。

而且,对于您的简单示例,4MB确实像是没有使用APC,否则它会真的有点高。


2
Doctrine在Doctrine_Record、Doctrine_Collection和Doctrine_Query上提供了一个free()函数,该函数消除了这些对象上的循环引用,使它们可以被垃圾回收机制释放。更多信息... 为了减少内存使用,您可以尝试使用以下代码:
  • $record->free(true) - 这将进行深度清理,还会在所有关系上调用free()
  • $collection->free() - 这将释放所有集合引用
  • Doctrine_Manager::connection()->clean()/clear() - 清理连接(并删除标识映射条目)
  • $query->free()

1

我猜测,大部分的内存都被Doctrine类加载所占用,而不是与查询本身相关的对象。

  • 你使用的是哪个版本的Doctrine?
  • 你在使用自动加载器吗?

在Doctrine 1.1中,默认的自动加载行为被称为“aggressive”,这意味着它会加载所有的模型类,即使你只在任何特定请求中使用一两个。将该行为设置为“conservative”可减少内存使用。


0

我刚用Symfony 1.4编写了“守护进程”脚本,并设置了以下选项来停止内存占用:

sfConfig::set('sf_debug', false);

如果你正在使用Doctrine+Symfony,这样做是很好的选择,但如果你只是使用Doctrine,则没有帮助。 - Jordan Feldstein
同意Ikon的观点,在Symfony中,分析器全局RAM。因此:在建立数据库连接之前设置sfConfig::set('sf_debug', false);将有助于大大提高性能,特别是如果您像我一样要将大型表转储为CSV。您可能希望在frontend_dev.php或类似文件(第三个参数)中执行此操作。 - user780302

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接