優(yōu)化Laravel數(shù)據(jù)庫查詢的18個技巧_第1頁
優(yōu)化Laravel數(shù)據(jù)庫查詢的18個技巧_第2頁
優(yōu)化Laravel數(shù)據(jù)庫查詢的18個技巧_第3頁
優(yōu)化Laravel數(shù)據(jù)庫查詢的18個技巧_第4頁
優(yōu)化Laravel數(shù)據(jù)庫查詢的18個技巧_第5頁
已閱讀5頁,還剩12頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第優(yōu)化Laravel數(shù)據(jù)庫查詢的18個技巧如果應用運行緩慢或存在大量數(shù)據(jù)庫查詢,請按照以下性能優(yōu)化提示來縮短應用的加載時間。

1.檢索大型數(shù)據(jù)集

本提示主要側(cè)重于提高處理大型數(shù)據(jù)集時應用的內(nèi)存使用率。

處理大的集合時,分組檢索結(jié)果處理,而不是一次性檢索處理。

如下展示了從posts表檢索數(shù)據(jù)的過程。

$posts=Post::all();//使用eloquent

$posts=DB::table(posts)-get();//使用查詢構(gòu)造器

foreach($postsas$post){

//處理posts操作

}

上面的例子會從posts表檢索所有的記錄并處理。如果這個表達到了100多萬行呢?內(nèi)存將很快被耗盡。

為了避免在處理大型數(shù)據(jù)集時出現(xiàn)問題,我們可以檢索結(jié)果子集并按照下面的方式處理它們。

選項1:使用chunk

//當使用eloquent時

$posts=Post::chunk(100,function($posts){

foreach($postsas$post){

//Processposts

//當使用查詢構(gòu)造器時

$posts=DB::table(posts)-chunk(100,function($posts){

foreach($postsas$post){

//Processposts

});

以上例子從posts表中檢索100條記錄對其進行處理,另外再檢索100條記錄進行處理。此迭代將繼續(xù),直到處理完所有記錄。

這種方法將創(chuàng)建更多的數(shù)據(jù)庫查詢,但內(nèi)存效率會更高。通常,大型數(shù)據(jù)集的處理應該再后臺進行。因此,可以在后臺運行時進行更多查詢,以避免在處理大型數(shù)據(jù)集時耗盡內(nèi)存。

選項2:使用游標

//使用eloquent

foreach(Post::cursor()as$post){

//處理單個post

//使用query構(gòu)建器

foreach(DB::table(posts)-cursor()as$post){

//處理單個post

}

示例進行單個數(shù)據(jù)庫查詢,檢索表的所有記錄,一個接一個一個處理Eloquent模型。這種方式僅查詢一次數(shù)據(jù)庫,得到全部posts。但使用php生成器優(yōu)化內(nèi)存使用。

什么情況使用這個呢?

這能夠在應用層極大地優(yōu)化內(nèi)存使用,由于我們檢索表的所有數(shù)據(jù),數(shù)據(jù)庫內(nèi)存占用任然很高。

在數(shù)據(jù)庫內(nèi)存較多,應用內(nèi)存較少的時候,建議使用游標。然而,如果你的數(shù)據(jù)庫沒有足夠的內(nèi)存,最好使用chunks。

選項3:使用chunkById

//使用eloquent

$posts=Post::chunkById(100,function($posts){

foreach($postsas$post){

//處理posts

//使用query構(gòu)造器

$posts=DB::table(posts)-chunkById(100,function($posts){

foreach($postsas$post){

//處理posts

});

chunk和chunkById最大的區(qū)別是chunk通過offset和limit檢索數(shù)據(jù)。然而

chunkById通過id字段檢索結(jié)構(gòu)。id字段通常是整型字段,而且它也是自增字段。

chunk和chunkById的查詢?nèi)缦隆?/p>

chunk

select*frompostsoffset0limit100

select*frompostsoffset101limit100

chunkById

select*frompostsorderbyidasclimit100

select*frompostswhereid100orderbyidasclimit100

通常,查詢使用limit和offset是較慢的,盡量避免使用。本文詳細介紹使用offset的問題。

chunkById使用id整型字段,通過whereclause查詢,這樣會更快。

什么時候使用chunkById?

當數(shù)據(jù)庫存在自增主鍵的時候使用。

2.選擇合適的列

通常從數(shù)據(jù)庫檢索數(shù)據(jù)時,會像下面這樣做。

$posts=Post::find(1);//使用eloquent

$posts=DB::table(posts)-where(id,=,1)-first();//使用query構(gòu)建器

上面的代碼會得到如下的查詢

select*frompostswhereid=1limit1

select*表示從表中查出所有列。

當需要所有列時,這沒有問題。

然而,僅需要指定的列(id,title)時,只需要像下面這樣檢索那些列。

$posts=Post::select([id,title])-find(1);//使用eloquent

$posts=DB::table(posts)-where(id,=,1)-select([id,title])-first();//使用query構(gòu)建器

上面代碼得到如下查詢

selectid,titlefrompostswhereid=1limit1

3.當需要數(shù)據(jù)庫表的一兩個列時

這點主要關(guān)注對檢索結(jié)果的處理時間。這不影響實際的查詢時間。

如我上面提到的,檢索指定的列,可以這樣做

$posts=Post::select([title,slug])-get();//使用eloquent

$posts=DB::table(posts)-select([title,slug])-get();//使用query構(gòu)建器

執(zhí)行上面的代碼,它會在幕后執(zhí)行以下操作。

執(zhí)行selecttitle,slugfromposts查詢

檢索出的每一行對應一個Post模型對象(對PHP對象)(query構(gòu)建器得到標準的PHP對象)

為Post模型生成collection

返回collection

訪問數(shù)據(jù)

foreach($postsas$post){

//$post是Post模型或php標準對象

$post-title;

$post-slug;

}

上面的方式有額外的開銷,為每一行創(chuàng)建Post模型,并為這些對象創(chuàng)建一個集合。如果的確需要Post模型實例而不是數(shù)據(jù),這是最正確的做法。

但如果您只需要兩個值時,則可以執(zhí)行以下操作:

$posts=Post::pluck(title,slug//使用eloquent時

$posts=DB::table(posts)-pluck(title,slug//使用查詢構(gòu)造器時

當上面代碼被執(zhí)行時,它在幕后會執(zhí)行以下操作。

對數(shù)據(jù)庫執(zhí)行selecttitle,slugfromposts查詢

創(chuàng)建一個數(shù)組,其中會以title作為數(shù)組值,slug作為數(shù)組鍵

返回數(shù)組(數(shù)組格式:[slug=title,slug=title])

要訪問結(jié)果,我們可以這么做

foreach($postsas$slug=$title){

//$title是post的title

//$slug是post的slug

}

如果您想檢索一列,您可以這么做

$posts=Post::pluck(title//使用eloquent時

$posts=DB::table(posts)-pluck(title//使用查詢構(gòu)造器時

foreach($postsas$title){

//$title是post的title

}

上面的方式消除了每一行Post對象的創(chuàng)建。這將降低查詢結(jié)果處理的內(nèi)存和時間消耗。

建議在新代碼中使用上述方式。個人感覺不值得花時間遵循上面的提示重構(gòu)代碼。

重構(gòu)代碼,最好是在要處理大的數(shù)據(jù)集或者是比較閑的時候

4.使用查詢代替collection來統(tǒng)計行數(shù)

統(tǒng)計表的行數(shù),通常這樣做

$posts=Post::all()-count();//使用eloquent

$posts=DB::table(posts)-get()-count();//使用查詢構(gòu)造器

這將生成以下查詢

select*fromposts

上述方法將從表中檢索所有行。將它們加載到collection對象中并計算結(jié)果。當數(shù)據(jù)表中的行較少時,這可以正常工作。但隨著表的增長,內(nèi)存很快就會耗盡。

與上述方法不同,我們可以直接計算數(shù)據(jù)庫本身的總行數(shù)。

$posts=Post::count();//使用eloquent時

$posts=DB::table(posts)-count();//使用查詢構(gòu)造器時

這將生成以下查詢

selectcount(*)fromposts

在sql中計算行數(shù)是一個緩慢的過程,當數(shù)據(jù)庫表中有多行時性能會很差。最好盡量避免計算行數(shù)。

5.通過即時加載關(guān)系避免n+1查詢

這條建議你可能聽說過無數(shù)次了。所以我會盡可能簡短。讓我們假設(shè)您有以下場景

classPostControllerextendsController

publicfunctionindex()

$posts=Post::all();

returnview(posts.index,[posts=$posts]);

}

//posts/index.blade.php文件

@foreach($postsas$post)

h3{{$post-title}}/h3

pAuthor:{{$post-author-name}}/p

/li

@endforeach

上面的代碼是檢索所有的帖子,并在網(wǎng)頁上顯示帖子標題和作者,假設(shè)帖子模型關(guān)聯(lián)作者。

執(zhí)行以上代碼將導致運行以下查詢。

select*fromposts//假設(shè)返回5條數(shù)據(jù)

select*fromauthorswhereid={post1.author_id}

select*fromauthorswhereid={post2.author_id}

select*fromauthorswhereid={post3.author_id}

select*fromauthorswhereid={post4.author_id}

select*fromauthorswhereid={post5.author_id}

如上,1條查詢來檢索帖子,5條查詢來檢索帖子的作者(假設(shè)有5篇帖子)。因此對于每篇帖子,都會進行一個單獨的查詢來檢索它的作者。

所以如果有N篇帖子,將會產(chǎn)生N+1條查詢(1條查詢檢索帖子,N條查詢檢索每篇帖子的作者)。這常被稱作N+1查詢問題。

避免這個問題,可以像下面這樣預加載帖子的作者。

$posts=Post::all();//Avoiddoingthis

$posts=Post::with([author])-get();//Dothisinstead

執(zhí)行上面的代碼得到下面的查詢:

select*fromposts//Assumethisqueryreturned5posts

select*fromauthorswhereidin({post1.author_id},{post2.author_id},{post3.author_id},{post4.author_id},{post5.author_id})

6.預加載嵌套關(guān)系

從上面的例子,考慮作者歸屬于一個組,同時需要顯示組的名字的情況。因此在blade文件中,可以按下面這樣做。

@foreach($postsas$post)

h3{{$post-title}}/h3

pAuthor:{{$post-author-name}}/p

pAuthorsTeam:{{$post-author-team-name}}/p

/li

@endforeach

接著

$posts=Post::with([author])-get();

得到下面的查詢:

select*fromposts//Assumethisqueryreturned5posts

select*fromauthorswhereidin({post1.author_id},{post2.author_id},{post3.author_id},{post4.author_id},{post5.author_id})

select*fromteamswhereid={author1.team_id}

select*fromteamswhereid={author2.team_id}

select*fromteamswhereid={author3.team_id}

select*fromteamswhereid={author4.team_id}

select*fromteamswhereid={author5.team_id}

如上,盡管預加載了authors關(guān)系,仍然產(chǎn)生了大量的查詢。這是因為沒有預加載authors上的team關(guān)系。

通過下面這樣來解決這個它。

$posts=Post::with([author.team])-get();

執(zhí)行得到下面的查詢。

select*fromposts//Assumethisqueryreturned5posts

select*fromauthorswhereidin({post1.author_id},{post2.author_id},{post3.author_id},{post4.author_id},{post5.author_id})

select*fromteamswhereidin({author1.team_id},{author2.team_id},{author3.team_id},{author4.team_id},{author5.team_id})

通過預加載嵌套關(guān)系,可以將查詢數(shù)從11減到3。

7.如果僅需要id時,別預加載belongsTo關(guān)系

想象一下,有posts和authors兩張表。帖子表有author_id列歸屬作者表。

為了得到帖子的作者id,通常這樣做

$post=Post::findOrFAIl(postid

$post-author-

執(zhí)行得到兩個查詢。

select*frompostswhereid=postidlimit1

select*fromauthorswhereid=postauthoridlimit1

然而,可以直接通過下面方式得到作者id。

$post=Post::findOrFail(postid

$post-author_id;//帖子表有存放作者id的author_id列

什么時候采取上面的方式?

采取上的方式,需要確保帖子關(guān)聯(lián)的作者在作者表始終存在。

8.避免使用不必要的查詢

很多時候,一些數(shù)據(jù)庫查詢是不必要的??纯聪旅娴睦?。

php

classPostControllerextendsController

publicfunctionindex()

$posts=Post::all();

$private_posts=PrivatePost::all();

returnview(posts.index,[posts=$posts,private_posts=$private_posts]);

}

上面代碼是從兩張不同的表(posts,private_posts)檢索數(shù)據(jù),然后傳到視圖中。

視圖文件如下。

//posts/index.blade.php

@if(request()-user()-isAdmin())

h2PrivatePosts/h2

@foreach($private_postsas$post)

h3{{$post-title}}/h3

pPublishedAt:{{$post-published_at}}/p

/li

@endforeach

/ul

@endif

h2Posts/h2

@foreach($postsas$post)

h3{{$post-title}}/h3

pPublishedAt:{{$post-published_at}}/p

/li

@endforeach

/ul

正如你上面看到的,$private_posts僅對管理員用戶可見,其他用戶都無法看到這些帖子。

問題是,當我們在做

$posts=Post::all();

$private_posts=PrivatePost::all();

我們進行兩次查詢。一次從posts表獲取記錄,另一次從private_posts表獲取記錄。

private_posts表的記錄僅管理員用戶可見。但我們?nèi)栽诓樵円詸z索所有用戶記錄,即使它們不可見。

我們可以調(diào)整邏輯,避免額外的查詢。

$posts=Post::all();

$private_posts=collect();

if(request()-user()-isAdmin()){

$private_posts=PrivatePost::all();

}

將邏輯更改為上述內(nèi)容后,我們對管理員用戶進行了兩次查詢,并對其他用戶進行了一次查詢。

9.合并相似的查詢

我們有時需要進行查詢以同一個表中檢索不同類型的行。

$published_posts=Post::where(status,=,published)-get();

$featured_posts=Post::where(status,=,featured)-get();

$scheduled_posts=Post::where(status,=,scheduled)-get();

上述代碼正從同一個表檢索狀態(tài)不同的行。代碼將進行以下查詢。

select*frompostswherestatus=published

select*frompostswherestatus=featured

select*frompostswherestatus=scheduled

如您所見,它正在對同一個表進行三次不同的查詢以檢索記錄。我們可以重構(gòu)此代碼以僅進行一次數(shù)據(jù)庫查詢。

$posts=Post::whereIn(status,[published,featured,scheduled])-get();

$published_posts=$posts-where(status,=,published

$featured_posts=$posts-where(status,=,featured

$sc

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論