【问题标题】:Optimize ram usage Laravel, split Cronjobs into chunks?优化 ram 使用 Laravel,将 Cronjobs 拆分成块?
【发布时间】:2020-07-18 03:10:03
【问题描述】:

我有一个 Laravel 后端,它每天从各种 API 获取数据并进行处理。有一个命令用于获取这些数据并将它们存储在数据库中,还有一个命令使用这些数据来更新 wordpress 字段。这是因为 laravel 只是我的 wordpress 网站的后端,而 laravel 负责更新我的在线商店的字段。

目前有 16.000 种产品在线。因此,使用从数据库中获取和存储的数据来更新 wordpress 中的字段的命令必须经过 16.000 个产品才能更新每个产品的字段。但是现在问题来了。我的主机限制了 128Mb 的内存。但是要完成更新 wordpress 字段的任务,我需要大约 280Mb。您可以在图中看到这里(x-axias 产品,y-axias ram 使用):

蓝线代表一个脚本,我通过原始 SQL 语句获取所有要更新的字段和产品,橙色代表一个脚本,我使用 laravel 模型来处理我想要更新的字段。除了 SQL 语句和 laravel 模型之外的一切都一样!

如您所见,SQL 语句以较少的 MB 开始,但在处理大量产品时不如使用 laravel 模型处理数据的版本高效。使用 laravel 模型的脚本稍微好一点。

使用 laravel 模型处理数据的版本是使用 laravel chunk 方法拆分模型。我想这也是橙色线上小缺口的答案。

我现在的问题是是否有可能在脚本执行之前对我想要处理的产品进行分块?这样垃圾收集器就可以从已经处理过数据块的脚本中删除数据,并清理内存。

是否有可能设置一个 cron 正在处理 0-5000 个产品,下一个 5000-10000 个产品等等。如果是这样,我该如何实施?垃圾收集器还会从内存中删除之前脚本中使用的所有变量吗?

我还会在下面粘贴我的代码,但我认为它没有真正的帮助。欢迎任何想法以尽量减少内存使用!

使用 laravel 模型的脚本代码:

public function handle(UpdateFields $updateFields, UpdateStatus $updateStatus, UpdateTerms $updateTerms)
{
  $count = 0;
  $chunkSize = 5000;
  $countProducts = Post::lastProductId();

  while($count < $countProducts){
      $products = Post::allProductsLiveWithMetas($count, $chunkSize);
      
      // Loop trough each product
      foreach($products as $product){
          // Update fields
          $updatedFields = $updateFields->setProduct($product)->update();

          // Update Status
          $status = $updateStatus->setProduct($product)->update();

          // Update Terms
          // Update Gender
          $gender = $updateTerms->setProduct($product)->set_gender();

          // Update Size
          $size = $updateTerms->setProduct($product)->set_size();
          
          // Update Sale
          $sale = $updateTerms->setProduct($product)->set_sale();

          // Delete Wordpress Cache to save from memory leakage
          wp_cache_delete($product->ID, 'post_meta');

          unset($product);
          unset($updatedFields);
          unset($status);
          unset($gender);
          unset($size);
          unset($sale);
      }

      unset($products);
      $count = $count + $chunkSize;
  }
}

亲切的问候

【问题讨论】:

  • 赞成将data 视为复数形式...如果您的 RAM 如此有限,那么对数据进行“分页”、分解来自数据库的请求是非常有意义的。或者使用游标不一次将所有内容拉回......在 PHP 中,它可能就像使用 fetch() 而不是 fetchAll() (缓冲/非缓冲)一样简单。抱歉,我不知道 wordpress。
  • 好吧,谢谢伙计。但这正是我正在做的。我“分页”/“分块”数据,但我在一个脚本中执行此操作,因此垃圾收集器不能真正丢弃旧变量。这让我想到了一个问题,如何动态地为每个数据块启动一个 cron?第一个 cron 处理 0-5000 个产品,第二个 cron 处理 5000-10000 个产品,依此类推。
  • 似乎是一个没有循环引用的奇怪问题。并且使用gc_ 函数似乎是在浪费时间......我将在下面的答案中给出几个选项。
  • 尝试使用 laravel 实现排队系统更新数据库,可以解决内存问题。 laravel.com/docs/7.x/queues供您参考。

标签: php wordpress laravel cron ram


【解决方案1】:

想到了两种方法...

  1. 链接请求,传入当前偏移量,增加,当脚本完成重定向并使用新偏移量再次调用。您的 CRON 调用偏移量为 0 的脚本来启动执行链。到达上限时停止。

  2. 编写一个简单的 Bash 脚本并让 CRON 触发它,运行您的 PHP 脚本。

#/bin/bash
php /srv/somepath/script.php 0 500
php /srv/somepath/script.php 500 500
php /srv/somepath/script.php 1000 500

# or wget http://example.com/script.php?offset=0&limit=500

【讨论】:

  • 由于超时,我无法使用 wget。但是,当使用 Laravel 的 cron 时,如何告诉脚本从某个偏移量开始。那是我的问题。
  • 如果我理解你的话,请搜索 argv 和 argc。
【解决方案2】:

使用 Redis 实现队列系统可以解决您的内存问题,并且还可以让您更好地控制从外部源获取数据并在数据库中更新数据的实现。在队列中,您将处理较少的数据,并通过重试选项一一处理,以确保所有操作都完成而不会失败。

供您参考

Laravel queue documentation

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-23
    • 2012-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-06
    • 2013-07-04
    相关资源
    最近更新 更多