Утечка памяти Laravel/Eloquent неоднократно восстанавливает одну и ту же запись

Я пытаюсь написать функцию 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
    }
}

И вывод этого script (выполняемый командой "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();" то использование памяти остается стабильным.

Есть ли у кого-нибудь представление о том, почему эта строка будет использовать такую ​​память, или узнать более умный способ выполнить то, что я пытаюсь сделать?

Спасибо за ваше время.

Ответы

Ответ 1

Я воссоздал ваш script и прошел через него с помощью отладчика, потому что я не мог понять, какая ужасная вещь может вызвать проблему такого типа памяти. Когда я вышел, я наткнулся на это:

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

Кажется, что каждый запрос, который вы запускаете в Laravel, хранится в постоянном журнале, что объясняет ваше увеличение использования памяти после каждого запроса. Чуть выше, это следующая строка:

if ( ! $this->loggingQueries) return;

Немного больше копания определило, что свойство loggingQueries установлено по умолчанию по умолчанию и может быть изменено с помощью метода disableQueryLog, поэтому это означает, что если вы вызываете:

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

прежде чем вы будете выполнять все ваши запросы, вы не увидите когда-либо увеличивающегося использования памяти; он решил проблему, когда я проверил свой тест на основе вашего примера кода. Когда вы закончите, если вы не хотите влиять на остальную часть приложения, вы можете вызвать

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

для записи в журнал.

Ответ 2

Я не могу сказать, почему он не освобождает память. Лучше всего следить за кодом и узнать, как он делает то, что он делает для этого. Или спросите Тейлора.

Что касается других вещей, которые вы можете сделать:

Кэш-запрос Если вы повторяете один и тот же запрос снова и снова, используйте кеш запросов. Это так же просто, как добавить ->remember($time_to_cache) к вашему запросу.

Сделать СУБД выполнять всю тяжелую работу. В идеале вы просто делаете insert into select выражение, но это становится волосатым когда вы перебираете базы данных. Вместо этого, пакет и select и вставлять запросы, чтобы вы делали меньше вызовов в базах данных и создавали меньше объектов. Это разгружает большую часть тяжелого подъема системы управления базами данных, которая, возможно, более эффективна при выполнении этих задач.