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

下載本文檔

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

文檔簡介

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

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

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

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

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

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

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

foreach($postsas$post){

//處理posts操作

}

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

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

選項(xiàng)1:使用chunk

//當(dāng)使用eloquent時(shí)

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

foreach($postsas$post){

//Processposts

//當(dāng)使用查詢構(gòu)造器時(shí)

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

foreach($postsas$post){

//Processposts

});

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

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

選項(xiàng)2:使用游標(biāo)

//使用eloquent

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

//處理單個(gè)post

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

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

//處理單個(gè)post

}

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

什么情況使用這個(gè)呢?

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

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

選項(xiàng)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是較慢的,盡量避免使用。本文詳細(xì)介紹使用offset的問題。

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

什么時(shí)候使用chunkById?

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

2.選擇合適的列

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

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

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

上面的代碼會(huì)得到如下的查詢

select*frompostswhereid=1limit1

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

當(dāng)需要所有列時(shí),這沒有問題。

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

$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.當(dāng)需要數(shù)據(jù)庫表的一兩個(gè)列時(shí)

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

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

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

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

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

執(zhí)行selecttitle,slugfromposts查詢

檢索出的每一行對(duì)應(yīng)一個(gè)Post模型對(duì)象(對(duì)PHP對(duì)象)(query構(gòu)建器得到標(biāo)準(zhǔn)的PHP對(duì)象)

為Post模型生成collection

返回collection

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

foreach($postsas$post){

//$post是Post模型或php標(biāo)準(zhǔn)對(duì)象

$post-title;

$post-slug;

}

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

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

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

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

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

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

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

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

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

foreach($postsas$slug=$title){

//$title是post的title

//$slug是post的slug

}

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

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

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

foreach($postsas$title){

//$title是post的title

}

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

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

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

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

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

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

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

這將生成以下查詢

select*fromposts

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

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

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

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

這將生成以下查詢

selectcount(*)fromposts

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

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

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

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)頁上顯示帖子標(biāo)題和作者,假設(shè)帖子模型關(guān)聯(lián)作者。

執(zhí)行以上代碼將導(dǎo)致運(yùn)行以下查詢。

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篇帖子)。因此對(duì)于每篇帖子,都會(huì)進(jìn)行一個(gè)單獨(dú)的查詢來檢索它的作者。

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

避免這個(gè)問題,可以像下面這樣預(yù)加載帖子的作者。

$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.預(yù)加載嵌套關(guān)系

從上面的例子,考慮作者歸屬于一個(gè)組,同時(shí)需要顯示組的名字的情況。因此在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}

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

通過下面這樣來解決這個(gè)它。

$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})

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

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

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

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

$post=Post::findOrFAIl(postid

$post-author-

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

select*frompostswhereid=postidlimit1

select*fromauthorswhereid=postauthoridlimit1

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

$post=Post::findOrFail(postid

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

什么時(shí)候采取上面的方式?

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

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

很多時(shí)候,一些數(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僅對(duì)管理員用戶可見,其他用戶都無法看到這些帖子。

問題是,當(dāng)我們?cè)谧?/p>

$posts=Post::all();

$private_posts=PrivatePost::all();

我們進(jìn)行兩次查詢。一次從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)容后,我們對(duì)管理員用戶進(jìn)行了兩次查詢,并對(duì)其他用戶進(jìn)行了一次查詢。

9.合并相似的查詢

我們有時(shí)需要進(jìn)行查詢以同一個(gè)表中檢索不同類型的行。

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

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

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

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

select*frompostswherestatus=published

select*frompostswherestatus=featured

select*frompostswherestatus=scheduled

如您所見,它正在對(duì)同一個(gè)表進(jìn)行三次不同的查詢以檢索記錄。我們可以重構(gòu)此代碼以僅進(jìn)行一次數(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等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論