【问题标题】:Laravel / Eloquent memory leak retrieving the same record repeatedlyLaravel / Eloquent内存泄漏重复检索相同的记录
【发布时间】:2013-09-17 12:05:09
【问题描述】:

我正在尝试编写一个 laravel 函数,该函数从一个数据库中获取大量记录(100,000+)并将其放入另一个数据库中。为此,我需要查询我的数据库并查看用户是否已经存在。我反复调用这段代码:

$users = User::where('id', '=', 2)->first();

然后在发生了几百次之后,我的内存耗尽了。所以,我做了一个使用所有可用内存的极简示例,它看起来像这样:

<?php

use Illuminate\Console\Command;

class memoryleak extends Command
{
    protected $name = 'command:memoryleak';
    protected $description = 'Demonstrates memory leak.';

    public function fire()
    {
        ini_set("memory_limit","12M");

        for ($i = 0; $i < 100000; $i++)
        {
            var_dump(memory_get_usage());
            $this->external_function();
        }
    }

    function external_function()
    {
        // Next line causes memory leak - comment out to compare to normal behavior
        $users = User::where('id', '=', 2)->first();

        unset($users);
        // User goes out of scope at the end of this function
    }
}

这个脚本的输出(由'php artisan command:memoryleak'执行)看起来像这样:

int(9298696)
int(9299816)
int(9300936)
int(9302048)
int(9303224)
int(9304368)
....
int(10927344)
int(10928432)
int(10929560)
int(10930664)
int(10931752)
int(10932832)
int(10933936)
int(10935072)
int(10936184)
int(10937320)
....
int(12181872)
int(12182992)
int(12184080)
int(12185192)
int(12186312)
int(12187424)
PHP Fatal error:  Allowed memory size of 12582912 bytes exhausted (tried to allocate 89 bytes) in /Volumes/Mac OS/www/test/vendor/laravel/framework/src/Illuminate/Database/Connection.php on line 275

如果我注释掉“$users = User::where('id', '=', 2)->first();”这一行则内存使用保持稳定。

有没有人知道为什么这条线会使用这样的内存,或者知道一种更聪明的方法来完成我想要做的事情?

感谢您的宝贵时间。

【问题讨论】:

    标签: laravel laravel-4


    【解决方案1】:

    我重新创建了您的脚本并使用调试器逐步完成了它,因为我无法理解哪种可怕的事情会导致这种类型的内存问题。当我走过时,我遇到了这个:

    // in Illuminate\Database\Connection
    $this->queryLog[] = compact('query', 'bindings', 'time');
    

    您在 Laravel 中运行的每个查询似乎都存储在一个持久日志中,这解释了您每次查询后内存使用量的增加。就在上面,是以下行:

    if ( ! $this->loggingQueries) return;
    

    稍加挖掘确定loggingQueries 属性默认设置为true,并且可以通过disableQueryLog 方法更改,也就是说,如果您调用:

     DB::connection()->disableQueryLog();
    

    在执行所有查询之前,您不会看到内存使用量不断增加;当我根据您的示例代码运行测试时,它解决了问题。完成后,如果您不想影响应用程序的其余部分,您可以调用

    DB::connection()->enableQueryLog();
    

    重新启用日志记录。

    【讨论】:

    • 您可以在关闭查询日志的情况下发布内存使用情况吗?这不是没有释放mysql结果的结果吧?
    • @fideloper :不,按照原始发布者的方式运行代码,加上禁用日志记录,可以防止不断增加的内存使用问题。 Laravel 正在释放 mySQL 结果。
    • 作为一种快捷方式,您也可以直接使用DB::disableQueryLog(),而不是检索连接对象。
    【解决方案2】:

    我不能说它为什么不释放内存。你最好的选择是遵循代码并了解它是如何为那个代码做的。或者问泰勒。

    至于你可以做的其他事情:

    缓存查询如果您一遍又一遍地调用同一个查询,请使用query cache。就像在查询中添加 -&gt;remember($time_to_cache) 一样简单。

    让 DBMS 完成所有繁重的工作。 理想情况下,您只需执行 an insert into select statement,但是当您跨数据库时,这会变得很麻烦。取而代之的是,批处理 select 和 the insert queries,这样您就可以减少对数据库的调用并创建更少的对象。这将更多繁重的工作转移到了数据库管理系统上,可以说在这些类型的任务中效率更高。

    【讨论】:

    • 这看起来像是一个展示行为的示例 - 没有人会根据硬编码的 ID 选择同一行 10,000 次
    • @Erik - 我只能通过他发布的内容,因为他没有发布他实际上想要做的事情,与导致的示例代码相同的细节内存泄漏。他说他希望将数据从一个数据库复制到另一个数据库,这就是我的回答。他还提到他需要检查用户是否确实存在,但没有任何其他信息,完全有可能多次引用同一个用户 ID(这是典型的 1:N 关系),在这种情况下,我的答案是完全相关。
    猜你喜欢
    • 2012-10-11
    • 2011-06-10
    • 2014-03-11
    • 2017-10-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    • 2016-06-04
    • 1970-01-01
    相关资源
    最近更新 更多