




版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 網(wǎng)絡(luò)普法考試試題及答案
- 工程招投標(biāo)管理與協(xié)議執(zhí)行流程規(guī)范
- 浙江國企招聘2025溫州市甌海旅游投資集團(tuán)有限公司及下屬子公司招聘10人筆試參考題庫附帶答案詳解
- 2025福建福州市建筑大數(shù)據(jù)技術(shù)有限公司招聘4人筆試參考題庫附帶答案詳解
- 2025河南鄭州二七區(qū)一國企招聘各部門人員9人筆試參考題庫附帶答案詳解
- 2025江蘇徐州東創(chuàng)新能源科技有限公司招聘19人筆試參考題庫附帶答案詳解
- 2025年合肥興泰金融控股(集團(tuán))有限公司招聘23人筆試參考題庫附帶答案詳解
- 2025山東芳蕾玫瑰科技開發(fā)有限公司招聘11人筆試參考題庫附帶答案詳解
- 幼兒園秋游安全教案
- 色彩理論在廣告設(shè)計(jì)中的試題及答案
- 外國畫家作品介紹賞析
- 巖土工程勘察報(bào)告
- 中藥養(yǎng)護(hù)記錄表
- 哈弗H5汽車說明書
- 音樂鑒賞(西安交通大學(xué))知到章節(jié)答案智慧樹2023年
- 2023年成都市新都區(qū)九年級(jí)二診英語試題(含答案和音頻)
- 金屬與石材幕墻工程技術(shù)規(guī)范-JGJ133-2013含條文說
- 分包合法合規(guī)宣貫(2017年6月)
- GB 18613-2020電動(dòng)機(jī)能效限定值及能效等級(jí)
- 《行政組織學(xué)結(jié)課論文綜述3000字》
- 2023年浙江省高中數(shù)學(xué)學(xué)業(yè)水平考試知識(shí)條目精校版
評(píng)論
0/150
提交評(píng)論