优化数据库查询以避免内存问题

时间:2018-09-12 16:17:55

标签: laravel-5 chunks

我有一个巨大的Users表(可能有4百万行左右),我想每月运行一次Job,以检查他们的上次登录日期,以及他们是否尚未登录update user并将isPassive列更新为true。

$users = \DB::table('users')
                   ->whereNull('isPassive')->get();
foreach($users as $user)
{
   if(!$user->wasActive())
   {
      $this_user = (new User)::where('id', $user->id)->first();
      $this_user->isPassive = true;
      $this_user->save();
   }
}

我已经学会了使用Laravel来运行作业,但这给了我内存问题。好了,我可以增加服务器的内存,但是根据我在这里所读到的内容,增加内存并不总是最好的解决方案。

我尝试了chunk results,但是当我使用where()时,它就挂了。如果我在没有和条件的情况下使用它,则效果很好。

我想知道有经验的开发人员将如何中断这项工作以使用更少的内存?

将其命名为Laravel,但这是任何新程序员在学习更好地管理内存时可能会遇到的问题。

谢谢您的建议

1 个答案:

答案 0 :(得分:0)

您的代码存在一些问题。首先:

$this_user = (new User)::where(...)->first();
...

此行在foreach循环的每次迭代中都运行一个新查询,因此实际上您在运行400万个其他不必要的查询。该查询是多余的,因为您已经拥有$user,并且$this_user将会是同一件事。更改您的代码,如下所示:

$users = User::whereNull('isPassive')->get();
foreach($users AS $user){
  if(!$user->wasActive()){
    $user->isPassive = true;
    $user->save();
  }
}

这样,您将查询减少了一半。

此外,我不确定wasActive()方法的作用,但是如果您可以将该逻辑移到初始查询中,则可以运行单个update语句来删除循环:

DB::table("users")
->whereNull("isPassive")
->where(...) // Apply logic from User wasActive() check
->update(["isPassive" => true]);

希望有帮助。