解决Laravle内存泄漏的问题

8 篇文章 1 订阅

最近对老项目进行了重构,需要将老版数据库里的数据迁移到新版的数据库,由于新旧两版的数据库表结构存在较大差异,所以写了php脚本来迁移数据。在Laravel项目下的app\Console\Commands目录下创建命令行类,之后在linux命令行执行 php artisan data:v2 运行

迁移代码很简单,只有几行,用到了laravle查询构建器的chunk方法:

public function familyUploadCount()
{
   
    $service = app(DataService::class);
    Scheduling::where('schedule_type', 4)
       ->where('id', '>', 363100)
       ->where('id', '<=', 477478)
       ->orderBy('id')
       ->select('id', 'student_ids')
       ->chunk(500,function($data)use($service){
           foreach ($data as $value) {                    
               if(isset($value->student_ids[0])){
                   $service->updateStudentScheduleData($value->student_ids[0], [
                       'family_upload_count' => 1,
                   ]);                        
               }
               echo $value->id."\n";
           }
           unset($data);
       });

}

chunk方法的源代码是根据id来排序,然后每次从数据库中取出500条数据,再传递给开发者定义的闭包函数处理。

/**
     * Chunk the results of the query.
     *
     * @param  int  $count
     * @param  callable  $callback
     * @return bool
     */
    public function chunk($count, callable $callback)
    {
        $this->enforceOrderBy();

        $page = 1;

        do {
            // We'll execute the query for the given page and get the results. If there are
            // no results we can just break and return from here. When there are results
            // we will call the callback with the current chunk of these results here.
            $results = $this->forPage($page, $count)->get();

            $countResults = $results->count();

            if ($countResults == 0) {
                break;
            }

            // On each chunk result set, we will pass them to the callback and then let the
            // developer take care of everything within the callback, which allows us to
            // keep the memory low for spinning through large result sets for working.
            if ($callback($results, $page) === false) {
                return false;
            }

            unset($results);

            $page++;
        } while ($countResults == $count);

        return true;
    }

但是在命令行运行一段时间后,却出现了内存耗尽的情况。抛出异常:
PHP Fatal error: Allowed memory size of 268 435 456 bytes exhausted
修改php.ini文件,将memory_limit设置为-1,一段时间后发现系统内存被占满,用top命令观察到此时脚本吃掉了系统所有内存,系统不得不启用swap分区,导致系统十分卡顿。

内存耗尽的可能原因

1. 程序读取了大文件,超出了内存限制;
2. 在程序内分配了大的变量没有及时unset;
3. 循环引用的问题,特别是当执行loop的时候,最容易产生这样的问题;

由于这里没有对大文件进行大写,只有一个循环,并且在循环里执行了数据库查询、更新操作,再结合laravel的单例模式,因此分析可能的原因是对象的某个属性一直在写入数据,常见的如数组 的 push操作,因此导致了属性不断变大,占用的内存越来越多。这里操作数据库用到了laravel的Model类,查看Model源码,发现每次查询和更新都会记录 日志 和发布事件。在运行脚本之前手动关闭:

\DB::disableQueryLog();
\DB::unsetEventDispatcher();
Model::unsetEventDispatcher();

重启脚本程序,此时程序正常运行,且内存不再增长,维持在2.6%
top查看内存使用情况
参考链接:
参考文章1
参考文章2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值