Symfony2和Doctrine2:查询对象时的高内存使用率

时间:2021-04-10 06:42:53

I have two (for this question relevant) Entities which are in a many (certificates) to one (policy) relationship:

我有两个(对于这个问题相关)实体,它们是多个(证书)到一个(策略)关系:

  • Certificate
  • Policy

...which basically look like this:

......基本上看起来像这样:

class Policy {
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\OneToMany(targetEntity="Akm\CertificateBundle\Entity\Certificate", mappedBy="policies")
     */
    private $certificates;

    /**
     * Get template (assuming there is only one
     * per policy (which should be the case) and return the Certificate
     * object. If (for some reason) there is more than only one template,
     * then only the first one will be returned.
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getTemplate() {
        $certificates = $this->getCertificates();
        foreach($certificates as $cert) {
           if ($cert->getIsTemplate()) {
              return $cert;
           }
        }
        return false;
    }
}

and:

class Certificate {
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="boolean", nullable=false)
     */
    private $isTemplate;

    /**
     * @ORM\ManyToOne(targetEntity="Akm\CertificateBundle\Entity\Policy", inversedBy="certificates")
     * @ORM\JoinColumn(name="policy_id", referencedColumnName="id", nullable=true)
     */
    private $policies;
}

(and of course the corresponding getters and setter, auto-generated by doctrine) The problem with this approach is, that if I call a controller, which itself calls the removeTemplate()-function, I get a Allowed memory size of 134217728 bytes exhausted (tried to allocate [...] bytes) in [...]; I think, that the problem is the foreach-loop, which fetches the certificate object and tests its property isTemplate. Because the certificate-objects are quite large, this results in too high memory usage (resulting in a error message) if there are about 80 certificates or more.

(当然还有相应的getter和setter方法的,自动生成的学说),这种方法的问题是,如果我叫一个控制器,它本身调用removeTemplate() - 函数,我得到的134217728个字节允许内存大小耗尽(试图在[...]中分配[...]字节);我认为,问题是foreach-loop,它获取证书对象并测试其属性isTemplate。由于证书对象非常大,如果大约有80个或更多证书,则会导致内存使用率过高(导致出现错误消息)。

Now I wonder, how I could solve this problem. I had two ideas / approaches, which didn't work as expected.

现在我想知道,我怎么能解决这个问题。我有两个想法/方法,没有按预期工作。

The first one was to unset() the $cert-variable as soon as I know, that I don't need it anymore:

第一个是在我知道后立即取消设置$ cert变量,我不再需要它了:

public function getTemplate() {
    $certificates = $this->getCertificates();
    foreach($certificates as $cert) {
       if ($cert->getIsTemplate()) {
          return $cert;
       } else {
          unset($cert);
       }
    }
    return false;
}

However, this didn't change anything, the controller still used the same amount of memory.

但是,这并没有改变任何东西,控制器仍然使用相同数量的内存。

So I thought about just replacing all calls of the getTemplate()-function inside the controllers with the following (where the $pol-variable is an object of the Policy-entity):

所以我想到只需用以下内容替换控制器内的getTemplate()函数的所有调用(其中$ pol-variable是Policy-entity的一个对象):

$template = $cert_repository->findBy(
                    array('isTemplate' => true),
                    array('policies' => $pol->getId());

(I used the $pol->getId() instead of just the Policy-object because of this *-answer)

(我使用$ pol-> getId()而不仅仅是Policy-object,因为这个*-answer)

The problem with this approach is, that I always get a Unrecognized field: policies-error and I don't know why.

这种方法的问题是,我总是得到一个无法识别的字段:策略错误,我不知道为什么。

I hope that someone can help me to get one of these approaches working (I would prefer the first one, because I don't have to change anything in the controllers then).

我希望有人可以帮助我让其中一种方法起作用(我更喜欢第一种方法,因为我不需要在控制器中改变任何东西)。

Thanks in advance, katze_sonne

提前谢谢,katze_sonne

1 个解决方案

#1


0  

Two solutions:

  • You could paginate your results
  • 您可以对结果进行分页

  • You could hydrate your results into arrays
  • 您可以将结果保存到数组中

You could do something like this

你可以这样做

$query = $this->cert_repository
              ->createQueryBuilder('p')
              ->select(array('p'))
              ->where('p.isTemplate = :isTemplate')->setParameter('isTemplate', true)
              ->andWhere('p.policies = :policies')->setParameter('policies', $pol->getId())
              ->getQuery()
              ->setFirstResult($offset)
              ->setMaxResults($limit);
return $query->getArrayResult();

Basically, when Doctrine hydrates objects, it keeps reference of the object's data into memory and using the function unset will not free the memory, however hydrating the objects into arrays lets you use the function unset.

基本上,当Doctrine水合对象时,它会将对象的数据引用到内存中,并且使用unset函数不会释放内存,但是将对象保存到数组中可以让您使用未设置的函数。

#1


0  

Two solutions:

  • You could paginate your results
  • 您可以对结果进行分页

  • You could hydrate your results into arrays
  • 您可以将结果保存到数组中

You could do something like this

你可以这样做

$query = $this->cert_repository
              ->createQueryBuilder('p')
              ->select(array('p'))
              ->where('p.isTemplate = :isTemplate')->setParameter('isTemplate', true)
              ->andWhere('p.policies = :policies')->setParameter('policies', $pol->getId())
              ->getQuery()
              ->setFirstResult($offset)
              ->setMaxResults($limit);
return $query->getArrayResult();

Basically, when Doctrine hydrates objects, it keeps reference of the object's data into memory and using the function unset will not free the memory, however hydrating the objects into arrays lets you use the function unset.

基本上,当Doctrine水合对象时,它会将对象的数据引用到内存中,并且使用unset函数不会释放内存,但是将对象保存到数组中可以让您使用未设置的函数。