在 Laravel 中从我的数据库中循环超过 1000 个条目需要很长时间
It takes a long time to loop over 1000 entries from my database in Laravel
我正在设置一个 api 端点,负责从我的数据库中获取电影。我与流派有着多对多的关系。它们与枢轴 table 相连。在通过响应发送之前,我试图按类型对所有电影进行分组。 Chrome 显示它需要 7+s TTFB(第一个字节的时间)。我需要知道减速发生在哪里。我还使用 Postman 测试了端点并显示了相同的结果。我不知道我是否在处理关系、遍历数据库、获取数据方面做错了什么。
我正在使用 Laradock 来提供我的 Mysql、PHP、NGINX。我试图打破循环,这样每个流派组只能包含 10 部电影。这样做加快了我的时间,从 20+s -> 7+s。
有:
11个流派
1300 部电影
3205genre_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)
];
});
流派播种机
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 播种器
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::get('movies/filteredByGenre', 'MovieController@filteredByGenre');
MovieController@filteredByGenre
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 秒。
如果您的 none 个表已编入索引,我会从那里开始。您通常可以通过在主列(本例中为 ID)上添加索引 and/or 主键来大幅提升性能。你可以把它们想象成一本书的索引……当然你可以扫描每一页,但如果你有一张地图,它会更快。
Schema::create('movies', function (Blueprint $table) {
$table->bigIncrements('id')->primary();
... or ...
$table->index(['id']);
});
我还建议从 https://github.com/barryvdh/laravel-debugbar 安装或启用 Laravel 调试栏。这可以向您展示您的应用程序在幕后发生了什么,包括进行的每个数据库查询以及完成这些查询需要多长时间。有点像 Dev Tools 中的 TTFB,但是 Chrome 看不到。
我经常使用的最后一个选项是 Laravel 生成我的数据库查询但不执行它...
$movies = Movie::with('genre:genre')->toSql();
... 然后直接在我的数据库中执行它(phpMyAdmin 或您首选的控制台)如果速度很快,我知道它是我的 php 代码。否则我知道我需要查看其他数据库优化。
问题是我如何从数据库中检索数据。我以前使用的方式是检索数据的时间太长。我将查询切换为:
$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 可以很轻松地处理这些数据量。
我正在设置一个 api 端点,负责从我的数据库中获取电影。我与流派有着多对多的关系。它们与枢轴 table 相连。在通过响应发送之前,我试图按类型对所有电影进行分组。 Chrome 显示它需要 7+s TTFB(第一个字节的时间)。我需要知道减速发生在哪里。我还使用 Postman 测试了端点并显示了相同的结果。我不知道我是否在处理关系、遍历数据库、获取数据方面做错了什么。
我正在使用 Laradock 来提供我的 Mysql、PHP、NGINX。我试图打破循环,这样每个流派组只能包含 10 部电影。这样做加快了我的时间,从 20+s -> 7+s。
有: 11个流派 1300 部电影 3205genre_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)
];
});
流派播种机
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 播种器
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::get('movies/filteredByGenre', 'MovieController@filteredByGenre');
MovieController@filteredByGenre
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 秒。
如果您的 none 个表已编入索引,我会从那里开始。您通常可以通过在主列(本例中为 ID)上添加索引 and/or 主键来大幅提升性能。你可以把它们想象成一本书的索引……当然你可以扫描每一页,但如果你有一张地图,它会更快。
Schema::create('movies', function (Blueprint $table) {
$table->bigIncrements('id')->primary();
... or ...
$table->index(['id']);
});
我还建议从 https://github.com/barryvdh/laravel-debugbar 安装或启用 Laravel 调试栏。这可以向您展示您的应用程序在幕后发生了什么,包括进行的每个数据库查询以及完成这些查询需要多长时间。有点像 Dev Tools 中的 TTFB,但是 Chrome 看不到。
我经常使用的最后一个选项是 Laravel 生成我的数据库查询但不执行它...
$movies = Movie::with('genre:genre')->toSql();
... 然后直接在我的数据库中执行它(phpMyAdmin 或您首选的控制台)如果速度很快,我知道它是我的 php 代码。否则我知道我需要查看其他数据库优化。
问题是我如何从数据库中检索数据。我以前使用的方式是检索数据的时间太长。我将查询切换为:
$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 可以很轻松地处理这些数据量。