REST API에 대한 Larabel DB 쿼리가 매우 느립니다.
라라벨과 함께 첫 걸음을 내딛고 음악 재생 목록을 작성하려고 합니다.데이터베이스에는 다음 3개의 엔티티/모델이 있습니다.
Play <-n-1-> Song <-n-1-> Artist
테이블 생성:
Schema::create('plays', function (Blueprint $table) {
$table->id();
$table->bigInteger('song_id')->unsigned();
$table->foreign('song_id')
->references('id')
->on('songs')
->onDelete('cascade');
$table->dateTimeTz('date');
$table->bigInteger('station_id')->unsigned();
$table->foreign('station_id')
->references('id')
->on('stations');
$table->timestamps();
$table->unique(['song_id', 'date']);
});
Schema::create('songs', function (Blueprint $table) {
$table->id();
$table->bigInteger('artist_id')->unsigned();
$table->foreign('artist_id')
->references('id')
->on('artists')
->onDelete('cascade');
$table->string('title');
$table->string('cover_url')->nullable();
$table->smallInteger('cover_width')->nullable();
$table->smallInteger('cover_height')->nullable();
$table->string('asin')->nullable();
$table->date('last_cover_check')->nullable();
$table->timestamps();
$table->unique(['artist_id', 'title']);
});
Schema::create('artists', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->timestamps();
});
데이터베이스에는 약 85만6000곡의 플레이와 33000곡의 곡, 15000명의 아티스트가 있습니다.
처음에는 plays-> songs-> artist를 열심히 로드하여 직접 모델을 사용하려고 했습니다.
return PlayResource::collection(Play::whereDate('date', $date)->get());
Larabel 경유 REST API에 대한 이 쿼리는 인섬니아를 사용하여 2.6초 걸립니다.이건 너무 느린 것 같아요.
Larabel을 올바르게 이해하면 많은 DB 쿼리에 영향을 미칩니다.
- 재생에 대한 단일 쿼리
- 노래에 대한 또 다른 노래
- 하지만 모든 연극/노래에 대해 아티스트에 대한 별도의 질문(?)이 있습니다.
그래서 하나의 JOIN 쿼리를 생성하여 하나의 DB 쿼리를 가지려고 했습니다.
return DB::table('plays')
->leftJoin('songs', 'plays.song_id', '=', 'songs.id')
->leftJoin('artists', 'songs.artist_id', '=', 'artists.id')
->select('plays.*', 'songs.*', 'artists.*')
->whereDate('plays.date', $date)
->orderByDesc('plays.date')
->get();
이 쿼리는 조금 빠르지만 여전히 느립니다: 2.2s
DB에서 직접 호출되는 동등한 SQL 쿼리가 훨씬 빠릅니다.
SELECT *
FROM plays p
LEFT JOIN songs s ON p.song_id = s.id
LEFT JOIN artists a ON s.artist_id = a.id
WHERE DATE(p.date) = "2020-12-30"
ORDER BY p.date DESC;
-> 0.4초
Laravel을 사용할 때 발생하는 일반적인 오버헤드입니까?
편집 1:
네, 찾았습니다.DB::getQueryLog()
(하지만 그건 내가 찾던 게 아니었어)이제 알겠다:신속한 로딩이 연결된 테이블을 3개 사용하면 DB 쿼리는 3개만 발생합니다.첫 번째는 400ms로 가장 느리다.2개의 쿼리는 각각 1~2ms밖에 걸리지 않습니다.
라라벨보다 빠를 루멘도 찾았어요.그래서 해봤어요.모델을 사용한 첫 번째 쿼리는 1.4초입니다.출력 매핑에 Json Resources를 추가하면 0.3초 -> 1.7초가 추가됩니다.내 생각에 이 모든 것은 여전히 느리다.
하지만 저는 이것이 현대적 프레임워크로 지불해야 하는 가격이라고 생각합니다.(2006년 이전 재생 목록에서는 프레임워크를 사용하지 않습니다.)
EDIT2: 첫 번째 쿼리는 다음과 같이 대폭 강화됩니다.
- 인덱스 추가
date
기둥. - 및 함수를 사용하지 않고 쿼리합니다(
DATE(`date`)
또는->whereDate('date', ...)
)를 대신해서->whereBetween('date', [$date, $date.' 23:59:59'])
이제 첫 번째 쿼리는 5ms(이전 400ms에서)밖에 걸리지 않습니다.루멘 질의는 모두 1.3점입니다
SQL 쿼리에 걸리는 시간은 현재 10ms 미만입니다.따라서 루멘 오버헤드는 약 99%입니다.통증이 아주 심해요.더 많은 튜닝 가능성을 찾아보겠습니다.
EDIT3 Rust(actix_web, diesel)를 사용하여 동일한 REST API를 작성했으며 동일한 DB를 사용하여 동일한 요청에는 16ms가 소요됩니다.이 PHP 프레임워크가 너무 느리다니 말도 안 돼.이제 Rust에 초점을 맞춰 백엔드의 PHP/Larabel/Lumen을 드롭하겠습니다.
Larabel이 데이터베이스에 대해 실행 중인 쿼리를 확인하려면 앱/프로바이더/AppServiceProvider.php 파일에 다음 함수를 추가합니다.
public function boot(){
if(env('APP_DEBUG')) {
DB::listen(function($query) {
File::append(
storage_path('/logs/query.log'),
$query->sql . ' [' . implode(', ', $query->bindings) . ']' . PHP_EOL
);
});
}
}
작성되는 /storage/logs/query.log 파일에 각 쿼리의 SQL 코드가 있습니다.이를 통해 쿼리 빌더의 스테이트먼트를 이해하고 최적화할 수 있었습니다.때로는 비슷한 문제가 있었습니다.
언급URL : https://stackoverflow.com/questions/65576827/laravel-db-query-for-rest-api-very-slow
'itsource' 카테고리의 다른 글
정적 메모리 할당과 동적 메모리 할당의 차이 (0) | 2022.09.25 |
---|---|
composition api에서 vuex 모듈 getter를 사용하는 방법 (0) | 2022.09.25 |
레일 3 액티브 레코드:연관 개수별 주문 (0) | 2022.09.25 |
MySQL 업데이트가 인덱스 조건 푸시다운을 지원하지 않습니까? (0) | 2022.09.25 |
안쪽을 클릭할 때 드롭다운 메뉴가 닫히지 않도록 합니다. (0) | 2022.09.25 |