Требуется много времени, чтобы зациклить более 1000 записей из моей базы данных в Laravel - PullRequest
2 голосов
/ 30 мая 2019

Я устанавливаю конечную точку API, которая отвечает за получение фильмов из моей базы данных.У меня много-много отношений с жанром.Они связаны с сводной таблицей.Я пытаюсь сгруппировать все фильмы по жанрам, прежде чем отправить их по ответу.Chrome показывает, что он принимает 7 + s TTFB (время до первого байта).Мне нужно знать, где происходит замедление.Я также проверил конечную точку с Почтальоном и показал те же результатыЯ не знаю, делаю ли я что-то не так с отношениями, перебираю базу данных, получаю данные.

Я использую Laradock для предоставления моего Mysql, PHP, NGINX.Я попытался выйти из цикла, чтобы в каждой жанровой группе было только 10 фильмов.Это ускорило мое время на 20 + с -> 7 + с.

. Имеется: 11 жанров 1300 фильмов 3205 genre_movie

Пример базы данных фильма:

 |id|name|...|
 |1 |mov1|...|
 |2 |mov2|...|
 |3 |mov3|...|
 |4 |mov4|...|

жанр:

|id|genre   |...|
|1 |action  |...|
|2 |drama   |...|
|3 |thriller|...|
|4 |cartoon |...|

сводка: genre_movie

|movie_id|genre_id|
|1       |1       |
|1       |2       |
|2       |2       |
|3       |4       |

Вот мои отношения Жанр:

    public function movie(){
        return $this->belongsToMany('App\Movie');
    }

Фильм:

    public function genre(){
        return $this->belongsToMany('App\Genre');
    }

Вот мои фильмы о миграции:

    public function up()
    {
        Schema::create('movies', function (Blueprint $table) {
            $table->bigIncrements('id')->unsigned();
            $table->string('title', 100);
            $table->text('synopsis');
            $table->integer('released_year');
            $table->string('imdb_url', 100);
            $table->string('s3_location', 100);
            $table->string('poster_location', 100);
            $table->boolean('isRestricted');
            $table->timestamps();
        });
    }

жанры:

    public function up()
    {
        Schema::create('genres', function (Blueprint $table) {
            $table->bigIncrements('id')->unsigned();
            $table->string('genre');
            $table->longText('description');
        });
    }

genre_movie:

    public function up()
    {
        Schema::create('genre_movie', function (Blueprint $table) {
            $table->bigInteger('genre_id')->unsigned();
            $table->foreign('genre_id')->references('id')->on('genres');

            $table->bigInteger('movie_id')->unsigned();
            $table->foreign('movie_id')->references('id')->on('movies');
        });

Вот как я заполняю свои данные: Фабрика кино

$factory->define(App\Movie::class, function (Faker $faker) {
    $faker->addProvider(new Image($faker));
    $faker->addProvider(new Base($faker));
    return [
        //
        'title' => $faker->name,
        'synopsis' => $faker->paragraph,
        'poster_location' => $faker->imageUrl($width=680, $height=680),
        'imdb_url' => 'https://www.imdb.com/title/tt5884052/',
        's3_location' => 'movie.mp4',
        'released_year' => $faker->numberBetween($min=1900, $max=1960),
        'isRestricted' => $faker->numberBetween($min=0, $max=1)
    ];
});

GenreTable Seeder

    public function run()
    {
        //

        $genres = ['action', 'adventure', 'comedy', 'crime','drama','fantasy','historical','horror','romance','science fiction','thriller'];
        $seeds = [];
        foreach($genres as $genre){
            array_push($seeds,[
                'genre' => $genre,
                'description' => Str::random(150)
            ]);

        }
        DB::table('genres')->insert($seeds);

    }

MovieTable Seeder

    public function run()
    {
        //
        $this->call([GenreSeeder::class]);
        factory(App\Movie::class, 1300)->create();

        $genres = App\Genre::all();

        App\Movie::all()->each(function ($movie) use ($genres) {
            $movie->genre()->attach(
                $genres->random(rand(1,4))->pluck('id')->toArray()
            );
        });

    }

Api Route

    Route::get('movies/filteredByGenre', 'MovieController@filteredByGenre');

MovieController @ FilterByGenre

    public function filteredByGenre(Request $request){

        $movies = Movie::with('genre:genre')->get();
        $sizeofMovies = count($movies);
        $formatedMovie = [];

        $count = 0;
        for($x = 0; $x < $sizeofMovies; $x++){
            $sizeofGenre = count($movies[$x]->genre);
            for($y = 0; $y < $sizeofGenre; $y++){
                $genre = $movies[$x]->genre[$y];
                try{
                    if(isset($formatedMovie[$genre['genre']])){
                        if(sizeof($formatedMovie[$genre['genre']]) > 10){
                            break;
                        }
                        $formatedMovie[$genre['genre']][] = $movies[$x];
                    }else{
                        $formatedMovie[$genre['genre']][] = $movies[$x];
                    }
                } catch(ErrorException $e) {
                    $formatedMovie[$genre['genre']][] = $movies[$x];
                }

            }
        }
        $response = ['success' => true, 'data' => $formatedMovie ];
        return response()->json($response, 201);
    }

Как я получаю данные во внешнем интерфейсе

    componentDidMount() {
        var url = '/api/movies/filteredByGenre';
        axios
            .get(url)
            .then(response => {
                return response.data;
            })
            .then(json => {
                console.log(json);
                this.setState({ frontPageMovies: json.data });
            });
    }

Команды для запуска моего сервера и заполнения кода

docker-compose up -d nginx mysql phpmyadmin workspace
php artisan db:seed --class=MoviesTableSeeder

Я ожидаю, что фильмы отсортированы по жанруВремя, необходимое для извлечения данных, составляет не более 1-3 с.

Ответы [ 2 ]

0 голосов
/ 31 мая 2019

Проблема в том, как я получил данные из базы данных. То, как я использовал раньше, занимало слишком много времени, чтобы получить данные. Я переключил запрос на:

        $movies = DB::table('genre_movie')
        ->select('movies.*')
        ->addSelect('genres.genre')
        ->join('movies','genre_movie.movie_id','=','movies.id')
        ->join('genres','genre_movie.genre_id','=','genres.id')
        ->get();

Я слишком новичок, чтобы понять почему. Я думаю, что именно так я настроил свою базу данных, модель или отношения; Я не уверен. Теперь моя страница загружается менее чем за 1 секунду.

Если кто-то может прокомментировать, почему это происходит, это было бы полезно. Я знаю, что eloquent может очень легко справиться с таким количеством данных.

0 голосов
/ 30 мая 2019

Если ни одна из ваших таблиц не проиндексирована, я бы начал там.Зачастую вы можете значительно повысить производительность, просто добавив индексы и / или первичные ключи в ваши первичные столбцы (идентификаторы в данном случае).Вы можете думать о них как об указателе книги ... конечно, вы можете сканировать каждую страницу, но намного быстрее, если у вас есть карта, где все есть.

Schema::create('movies', function (Blueprint $table) {
    $table->bigIncrements('id')->primary();
    ... or ...
    $table->index(['id']);
});

Я бытакже предложите установить или включить панель отладки Laravel с https://github.com/barryvdh/laravel-debugbar.. Это может показать вам, что происходит в вашем приложении за кулисами, включая каждый запрос к базе данных, который был сделан, и сколько времени они потребовались для выполнения.Вроде как TTFB в Dev Tools, но Chrome не видит.

Последний вариант, который я часто использую, - это чтобы Laravel генерировал мой запрос к базе данных, но не выполнял его ...

$movies = Movie::with('genre:genre')->toSql();

... и затем выполняю его прямо в моей базе данных (phpMyAdmin или предпочитаемой вами консоли). Если он там быстрый, я знаю, что это мой php-код.В противном случае я знаю, что мне нужно посмотреть на другие оптимизации БД.

...