首页 > 解决方案 > 在遍历 Drupal 8 EntityQuery 结果时达到 PHP 内存限制。我该如何保持下去?

问题描述

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

我知道 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
内存(build_query):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 清理旧的引用,每次迭代的内存消耗不应该保持稳定吗?

您会注意到我的端点仅以 344 MB 结束。实际的错误是在 Drupal 核心的某个地方抛出的。由于我想将最大 PHP 内存保持在 128M,我仍然需要降低我的部分内存。

标签: phpmemorymemory-managementdrupal-8

解决方案


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

来自 Drupal 8 文档:

函数 node_load_multiple

从数据库加载节点实体。

当您需要从数据库中加载多个节点时,应使用此功能。节点被加载到内存中,如果在同一页面请求期间再次加载,则不需要访问数据库。 [资源]

似乎它们旨在在整个页面请求期间持续存在,这会使内存消耗即使在迭代时也会累积。

实际上,我在 Drupal 论坛上看到很多其他开发人员的帖子在使用此功能时也遇到了内存不足的问题。如果加载许多节点,内存消耗会特别高。


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

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

希望有帮助:)


编辑:

嗯,似乎我们在尝试重置缓存时走在了正确的轨道上,但我们将不得不尝试另一种方法来重置它。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));
}

推荐阅读