代码之家  ›  专栏  ›  技术社区  ›  Jeff

迭代Drupal 8 EntityQuery结果时达到PHP内存限制。我该怎么保持低调?

  •  1
  • Jeff  · 技术社区  · 6 年前

    我有一个D8API端点,它查询特定的内容类型,应用任何可选条件,将结果转换为JSON,并返回到客户机。我将php内存限制更新到了512M,但仍在运行中。Drupal中只有1500条记录,所以不应该有任何理由说明它如此糟糕(每条记录341KB)?!!)如果我只是不断地提升内存以使其运行,则呈现的JSON小于2MB。

    我知道PHP垃圾收集是自动的,所以我想这里有一些引用被保留着。

    我曾多次尝试将其保持在较低的水平,例如批处理查询、重构为函数以及显式调用 gc_collect_cycles 但没有什么能改变。

    在对Drupal EntityQuery的结果进行迭代时,如何降低内存消耗?

      protected function get() {
        echo "memory (start): " . memory_get_usage() . "\n<br>";
    
        //some setup and validation
    
        $query = $this->build_query($params);
        echo "memory (build_query): " . memory_get_usage() . "\n<br>";
    
        $results = $query->execute();
        echo "memory (execute): " . memory_get_usage() . "\n<br>";
    
        $items = [];
    
        $chunk_size = 50;
        $chunks = array_chunk(array_values($results), $chunk_size);
        echo "memory (chunk): " . memory_get_usage() . "\n<br>";
    
        foreach ($chunks as $chunk) {
          $items = array_merge($items, $this->load_nodes($chunk));
          echo "memory (chunk loaded): " . memory_get_usage() . "\n<br>";
        }
        echo "memory (all loaded): " . memory_get_usage() . "\n<br>";
    
        $response = [ 'results' => $items ];
        return new ResourceResponse($response);
      }
    

      protected function load_nodes($ids) {
        $items = [];
        $nodes = node_load_multiple($ids);
        foreach ($nodes as $node) {
          $items[] = $this->transform($node); 
        }
        return $items;
      }
    

      protected function transform($array) {
        $new = [
          "field1" => $array['field1'],
          "field2" => $array['field2'],
          //... for about 30 more fields, with some processing/manipulation ...
        ];
        return $new;
      }
    

    关于存储器回波的输出是:

    内存(开始):28297032
    内存(内部版本查询):29984168
    内存(执行):31004048
    内存(块):31083864
    内存(块加载):42175976
    内存(块加载):50447792
    内存(块加载):57609344
    内存(块加载):66762688
    内存(块加载):74555712
    内存(块加载):86663016
    内存(块加载):98514192
    内存(块加载):110908336
    内存(块加载):122792592
    内存(块加载):134651328
    内存(块加载):145622512
    内存(块加载):156546072
    内存(块加载):167805352
    内存(块加载):178617040
    内存(块加载):190400936
    内存(块加载):201246256
    内存(块加载):212387384
    内存(块加载):223756088
    内存(块加载):234898632
    内存(块加载):246125624
    内存(块加载):257136304
    内存(块加载):268205304
    内存(块加载):278744896
    内存(块加载):289693184
    内存(块加载):300491840
    内存(块加载):310564624
    内存(块加载):321204064
    内存(块加载):333842760
    内存(块加载):343723672
    内存(块加载):344960728
    内存(全部加载):344960728

    每次迭代的内存消耗是否应该保持稳定? load_nodes 当GC清理旧的引用时?

    您会注意到我的端点只有344MB。实际上错误是在Drupal核心的某个地方抛出的。因为我想把最大的PHP内存保持在128m,所以我仍然需要把内存的一部分取下来。

    2 回复  |  直到 6 年前
        1
  •  1
  •   DrewT    6 年前

    实际上,我认为在这种情况下,您关于垃圾收集的假设是不正确的。

    从Drupal 8文档:

    功能节点加载多个

    从数据库加载节点实体。

    每当需要加载多个函数时,都应使用此函数 数据库中的节点。 节点已加载到内存中,但不会 如果在同一页请求期间再次加载,则需要数据库访问。 [source]

    似乎它们是为了在页面请求的整个过程中保持不变,这将使内存消耗累积,即使是迭代。

    实际上,我在Drupal论坛上看到了很多其他开发人员在使用这个函数时也遇到了内存不足的问题。如果加载多个节点,内存消耗将特别高。


    为了降低内存消耗,我建议通过将cache reset参数设置为true来禁用节点加载的缓存。例子:

    $nodes = node_load_multiple($ids, NULL, TRUE);
    

    希望有帮助:)


    编辑:

    hrmm我们似乎在正确的轨道上尝试重置缓存,但我们必须尝试另一种方法来重置它。这种方法是从弃用的 node_load() 功能。

    Drupal中备用重置缓存方法的类路径是:

    \Drupal::entityManager()->getStorage('node')->resetCache(array('NID'));
    

    固定脚本如下:

    $query = \Drupal::entityQuery('node')
         ->condition($params);
    
    $results = $query->execute();
    
    $nids = array_keys($results);
    
    foreach ($nids as $nid) {
        $node = \Drupal\node\Entity\Node::load($nid);
    
        // Do stuff with loaded node, ex:
        // print $node->title->value;
    
        // Now reset the cache with the legacy reset cache
        \Drupal::entityManager()->getStorage('node')->resetCache(array($nid));
    }
    
        2
  •  0
  •   Tagarikdi Djakouba    6 年前

    我认为您对PHP中的垃圾收集器有一个误解。

    好吧,垃圾收集器释放内存空间的唯一方法是当这个空间不再被任何变量引用时,同时,您总是从函数返回值,以便在高级函数中其他变量引用这些值。

    您还可以检查如何禁用Drupal中的某些缓存,这可能会对您有所帮助,具体取决于Drupal使用的缓存状态。