溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Laravel8優(yōu)化數(shù)據(jù)庫(kù)查詢(xún)的技巧有哪些

發(fā)布時(shí)間:2022-12-27 09:27:18 來(lái)源:億速云 閱讀:130 作者:iii 欄目:編程語(yǔ)言

本篇內(nèi)容介紹了“Laravel8優(yōu)化數(shù)據(jù)庫(kù)查詢(xún)的技巧有哪些”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

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

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

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

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

$posts = Post::all(); // 使用 eloquent
$posts = DB::table('posts')->get(); // 使用查詢(xún)構(gòu)造器
 foreach ($posts as $post){
 // 處理 posts 操作
}

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

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

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

// 當(dāng)使用 eloquent 時(shí)
$posts = Post::chunk(100, function($posts){
    foreach ($posts as $post){
     // Process posts
    }
});
 // 當(dāng)使用查詢(xún)構(gòu)造器時(shí)
$posts = DB::table('posts')->chunk(100, function ($posts){
    foreach ($posts as $post){
     // Process posts
    }
});

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

這種方法將創(chuàng)建更多的數(shù)據(jù)庫(kù)查詢(xún),但內(nèi)存效率會(huì)更高。 通常, 大型數(shù)據(jù)集的處理應(yīng)該再后臺(tái)進(jìn)行。因此,可以在后臺(tái)運(yùn)行時(shí)進(jìn)行更多查詢(xú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ù)庫(kù)查詢(xún),檢索表的所有記錄,一個(gè)接一個(gè)一個(gè)處理 Eloquent 模型。這種方式僅查詢(xún)一次數(shù)據(jù)庫(kù),得到全部 posts 。 但使用 php 生成器 優(yōu)化內(nèi)存使用。

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

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

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

選項(xiàng) 3:使用 chunkById

// 使用 eloquent
$posts = Post::chunkById(100, function($posts){
    foreach ($posts as $post){
     // 處理 posts
    }
});
 // 使用 query 構(gòu)造器
$posts = DB::table('posts')->chunkById(100, function ($posts){
    foreach ($posts as $post){
     // 處理 posts
    }
});

chunkchunkById 最大的區(qū)別是 chunk 通過(guò)offsetlimit 檢索數(shù)據(jù)。然而
chunkById 通過(guò)id 字段檢索結(jié)構(gòu)。id 字段通常是整型字段,而且它也是自增字段。

chunkchunkById 的查詢(xún)?nèi)缦隆?/p>

chunk

select * from posts offset 0 limit 100
select * from posts offset 101 limit 100

chunkById

select * from posts order by id asc limit 100
select * from posts where id > 100 order by id asc limit 100

通常,查詢(xún)使用 limit 和 offset 是較慢的,盡量避免使用。本文 詳細(xì)介紹使用 offset 的問(wèn)題。

chunkById 使用 id 整型字段,通過(guò) where clause 查詢(xún),這樣會(huì)更快。

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

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

2. 選擇合適的列

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

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

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

select * from posts where id = 1 limit 1

select * 表示從表中查出所有列。
當(dāng)需要所有列時(shí),這沒(méi)有問(wèn)題。

然而,僅需要指定的列(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)建器

上面代碼得到如下查詢(xún)

select id,title from posts where id = 1 limit 1

3. 當(dāng)需要數(shù)據(jù)庫(kù)表的一兩個(gè)列時(shí)

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

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

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

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

  • 執(zhí)行 select title, slug from posts 查詢(xún)

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

  • Post 模型生成 collection

  • 返回 collection

訪問(wèn)數(shù)據(jù)

foreach ($posts as $post){
    // $post 是 Post 模型或  php 標(biāo)準(zhǔn)對(duì)象
    $post->title;
    $post->slug;
}

上面的方式有額外的開(kāi)銷(xiāo),為每一行創(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'); // 使用查詢(xún)構(gòu)造器時(shí)

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

  • 對(duì)數(shù)據(jù)庫(kù)執(zhí)行 select title, slug from posts 查詢(xún)

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

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

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

foreach ($posts as $slug => $title){
    // $title 是 post 的 title
    // $slug 是 post 的 slug
}

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

$posts = Post::pluck('title'); // 使用 eloquent 時(shí)
$posts = DB::table('posts')->pluck('title'); // 使用查詢(xún)構(gòu)造器時(shí)
foreach ($posts as  $title){
    // $title 是 post 的 title
}

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

建議在新代碼中使用上述方式。個(gè)人感覺(jué)不值得花時(shí)間遵循上面的提示重構(gòu)代碼。
重構(gòu)代碼,最好是在要處理大的數(shù)據(jù)集或者是比較閑的時(shí)候

4. 使用查詢(xún)代替 collection 來(lái)統(tǒng)計(jì)行數(shù)

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

$posts = Post::all()->count(); // 使用 eloquent
$posts = DB::table('posts')->get()->count(); // 使用查詢(xún)構(gòu)造器

這將生成以下查詢(xún)

select * from posts

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

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

$posts = Post::count(); // 使用 eloquent 時(shí)
$posts = DB::table('posts')->count(); // 使用查詢(xún)構(gòu)造器時(shí)

這將生成以下查詢(xún)

select count(*) from posts

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

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

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

class PostController extends Controller
{
    public function index()
    {
        $posts = Post::all();
        return view('posts.index', ['posts' => $posts ]);
    }
}
// posts/index.blade.php 文件
 @foreach($posts as $post)
    <li>
        <h4>{{ $post->title }}</h4>
        <p>Author: {{ $post->author->name }}</p>
    </li>
@endforeach

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

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

select * from posts // 假設(shè)返回5條數(shù)據(jù)
select * from authors where id = { post1.author_id }
select * from authors where id = { post2.author_id }
select * from authors where id = { post3.author_id }
select * from authors where id = { post4.author_id }
select * from authors where id = { post5.author_id }

如上,1 條查詢(xún)來(lái)檢索帖子,5 條查詢(xún)來(lái)檢索帖子的作者(假設(shè)有 5 篇帖子)。因此對(duì)于每篇帖子,都會(huì)進(jìn)行一個(gè)單獨(dú)的查詢(xún)來(lái)檢索它的作者。

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

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

$posts = Post::all(); // Avoid doing this
$posts = Post::with(['author'])->get(); // Do this instead

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

select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )

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

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

@foreach($posts as $post)
    <li>
        <h4>{{ $post->title }}</h4>
        <p>Author: {{ $post->author->name }}</p>
        <p>Author's Team: {{ $post->author->team->name }}</p>
    </li>
@endforeach

接著

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

得到下面的查詢(xún):

select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )
select * from teams where id = { author1.team_id }
select * from teams where id = { author2.team_id }
select * from teams where id = { author3.team_id }
select * from teams where id = { author4.team_id }
select * from teams where id = { author5.team_id }

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

通過(guò)下面這樣來(lái)解決這個(gè)它。

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

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

select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )
select * from teams where id in( { author1.team_id }, { author2.team_id }, { author3.team_id }, { author4.team_id }, { author5.team_id } )

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

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

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

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

$post = Post::findOrFail(<post id>);
$post->author->id;

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

select * from posts where id = <post id> limit 1
select * from authors where id = <post author id> limit 1

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

$post = Post::findOrFail(<post id>);
$post->author_id; // 帖子表有存放作者 id 的 author_id 列

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

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

8. 避免使用不必要的查詢(xún)

很多時(shí)候,一些數(shù)據(jù)庫(kù)查詢(xún)是不必要的。看看下面的例子。

<?php
 class PostController extends Controller
{
    public function index()
    {
        $posts = Post::all();
        $private_posts = PrivatePost::all();
        return view('posts.index', ['posts' => $posts, 'private_posts' => $private_posts ]);
    }
}

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

// posts/index.blade.php
 @if( request()->user()->isAdmin() )
    <h3>Private Posts</h3>
    <ul>
        @foreach($private_posts as $post)
            <li>
                <h4>{{ $post->title }}</h4>
                <p>Published At: {{ $post->published_at }}</p>
            </li>
        @endforeach
    </ul>
@endif
 <h3>Posts</h3>
<ul>
    @foreach($posts as $post)
        <li>
            <h4>{{ $post->title }}</h4>
            <p>Published At: {{ $post->published_at }}</p>
        </li>
    @endforeach
</ul>

正如你上面看到的,$private_posts 僅對(duì) 管理員 用戶可見(jiàn),其他用戶都無(wú)法看到這些帖子。

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

$posts = Post::all();
$private_posts = PrivatePost::all();

我們進(jìn)行兩次查詢(xún)。一次從 posts 表獲取記錄,另一次從 private_posts 表獲取記錄。

private_posts 表的記錄僅 管理員用戶 可見(jiàn)。但我們?nèi)栽诓樵?xún)以檢索所有用戶記錄,即使它們不可見(jiàn)。

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

$posts = Post::all();
$private_posts = collect();
if( request()->user()->isAdmin() ){
    $private_posts = PrivatePost::all();
}

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

9. 合并相似的查詢(xún)

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

$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)行以下查詢(xún)。

select * from posts where status = 'published'
select * from posts where status = 'featured'
select * from posts where status = 'scheduled'

如您所見(jiàn),它正在對(duì)同一個(gè)表進(jìn)行三次不同的查詢(xún)以檢索記錄。我們可以重構(gòu)此代碼以?xún)H進(jìn)行一次數(shù)據(jù)庫(kù)查詢(xún)。

$posts =  Post::whereIn('status',['published', 'featured', 'scheduled'])->get();
$published_posts = $posts->where('status','=','published');
$featured_posts = $posts->where('status','=','featured');
$scheduled_posts = $posts->where('status','=','scheduled');
select * from posts where status in ( 'published', 'featured', 'scheduled' )

上面的代碼生成一個(gè)查詢(xún)來(lái)檢索全部特定狀態(tài)的帖子,通過(guò)狀態(tài)為返回的帖子創(chuàng)建不同的 collections 。三個(gè)不同的狀態(tài)的變量由一個(gè)查詢(xún)生成。

10. 為常查詢(xún)的列添加索引

如果查詢(xún)中含有 where 條件作用于 string 類(lèi)型的 column ,最好給這列添加索引。通過(guò)這列的查詢(xún)將會(huì)快很多。

$posts = Post::where('status','=','published')->get();

上面例子,我們對(duì) status 列添加 where 條件來(lái)查詢(xún)??梢酝ㄟ^(guò)下面這樣的數(shù)據(jù)庫(kù)遷移來(lái)優(yōu)化查詢(xún)。

Schema::table('posts', function (Blueprint $table) {
   $table->index('status');
});

11.  使用 simplePaginate 而不是 Paginate

分頁(yè)結(jié)果時(shí),我們通常會(huì)這樣做

$posts = Post::paginate(20);

這將進(jìn)行兩次查詢(xún),第一次檢索分頁(yè)結(jié)果,第二次表中計(jì)算表中的總行數(shù)。對(duì)表中的行數(shù)進(jìn)行計(jì)數(shù)是一個(gè)緩慢的操作,會(huì)對(duì)查詢(xún)性能產(chǎn)生負(fù)面影響。

那么為什么 laravel 會(huì)計(jì)算總行數(shù)呢?

為了生成分頁(yè)連接,Laravel 會(huì)計(jì)算總行數(shù)。因此,當(dāng)生成分頁(yè)連接時(shí),您可以預(yù)先知道會(huì)有多少頁(yè),以及過(guò)去的頁(yè)碼是多少。

另一方面,執(zhí)行 simplePaginate 不會(huì)計(jì)算總行數(shù),查詢(xún)會(huì)比 paginate 方法快得多。但您將無(wú)法知道最后一個(gè)頁(yè)碼并無(wú)法跳轉(zhuǎn)到不同的頁(yè)面。

如果您的數(shù)據(jù)庫(kù)表有很多行,最好避免使用 paginate,而是使用 simplePaginate。

$posts = Post::paginate(20); // 為所有頁(yè)面生成分頁(yè)鏈接
$posts = Post::simplePaginate(20); // 僅生成上一頁(yè)和下一頁(yè)的分頁(yè)鏈接

什么時(shí)候使用分頁(yè)和簡(jiǎn)單分頁(yè)

查看下面的比較表,確定是分頁(yè)還是簡(jiǎn)單分頁(yè)適合您


paginate / simplePaginate
數(shù)據(jù)庫(kù)表只有很少行,并且不會(huì)變大paginate / simplePaginate
數(shù)據(jù)庫(kù)表有很多行,并且增長(zhǎng)很快simplePaginate
必須提供用戶選項(xiàng)以跳轉(zhuǎn)到特定頁(yè)面paginate
必須向用戶顯示結(jié)果總數(shù)paginate
不主動(dòng)使用分頁(yè)鏈接simplePaginate
UI/UX 不會(huì)影響從切換編號(hào)分頁(yè)鏈接到下一個(gè)/上一個(gè)分頁(yè)鏈接simplePaginate
使用“加載更多”按鈕或“無(wú)限滾動(dòng)”分頁(yè)simplePaginate

12. 避免使用前導(dǎo)通配符(LIKE 關(guān)鍵字)

當(dāng)嘗試查詢(xún)匹配特性模式的結(jié)果時(shí),我們通常會(huì)使用

select * from table_name where column like %keyword%

上述查詢(xún)導(dǎo)致全表掃描。如果我們知道出現(xiàn)在列值開(kāi)頭的關(guān)鍵字,我們會(huì)查詢(xún)以下結(jié)果。

select * from table_name where column like keyword%

13. 避免 where 子句使用 SQL 函數(shù)

最好避免在 where 子句中使用 SQL 函數(shù),因?yàn)樗鼈儠?huì)導(dǎo)致全表掃描。 讓我們看下面的例子。要根據(jù)特定的時(shí)間查詢(xún)結(jié)果,我們通常會(huì)這樣做

$posts = POST::whereDate('created_at', '>=', now() )->get();

這將導(dǎo)致類(lèi)似的于下面的查詢(xún)

select * from posts where date(created_at) >= 'timestamp-here'

上面的查詢(xún)將導(dǎo)致全表掃描,因?yàn)樵谟?jì)算日期函數(shù)之前,不會(huì)應(yīng)用 where 條件。

我們可以重構(gòu)這個(gè)函數(shù),以避免使用如下的 date sql 函數(shù)

$posts = Post::where('created_at', '>=', now() )->get();
select * from posts where created_at >= 'timestamp-here'

14. 避免在表中添加過(guò)多的列

最好限制表中列的總數(shù)??梢岳孟?mysql 這樣的關(guān)系數(shù)據(jù)庫(kù)將具有如此多列的表拆分為多個(gè)表??梢允褂盟鼈兊闹麈I和外鍵將它們連接在一起。

向表中添加太多列會(huì)增加單個(gè)記錄的長(zhǎng)度,并且會(huì)減慢表掃描的速度。在執(zhí)行 select * 查詢(xún)時(shí),最終會(huì)檢索到一些實(shí)際上并不需要的列。

15. 將帶有文本數(shù)據(jù)的單獨(dú)列輸入到它們自己的表中

這個(gè)技巧來(lái)自個(gè)人經(jīng)驗(yàn),并不是設(shè)計(jì)數(shù)據(jù)庫(kù)表的標(biāo)準(zhǔn)方法。我建議只有當(dāng)您的表有太多的記錄或者會(huì)快速增長(zhǎng)時(shí)才遵循這個(gè)技巧。

如果一個(gè)表有存儲(chǔ)大量數(shù)據(jù)的列(例如: 數(shù)據(jù)類(lèi)型為 TEXT 的列) ,那么最好將它們分離到它們自己的表中,或者分離到一個(gè)不經(jīng)常被詢(xún)問(wèn)的表中。

當(dāng)表中有包含大量數(shù)據(jù)的列時(shí),單個(gè)記錄的大小會(huì)變得非常大。我個(gè)人觀察到它影響了我們其中一個(gè)項(xiàng)目的查詢(xún)時(shí)間。

假設(shè)您有一個(gè)名為 posts 的表,其中包含一列 內(nèi)容,用于存儲(chǔ)博客文章內(nèi)容。博客文章的內(nèi)容將是真正的巨大和經(jīng)常的時(shí)候,你需要這個(gè)數(shù)據(jù)只有當(dāng)一個(gè)人正在查看這個(gè)特定的博客文章。

所以,在數(shù)據(jù)表中有大量文章記錄的時(shí)候,將這些長(zhǎng)文本字段(大字段)分離到單獨(dú)的表中將會(huì)徹底的改善查詢(xún)性能。

16. 從表中查詢(xún)最新記錄的最佳實(shí)踐

當(dāng)需要從一個(gè)數(shù)據(jù)表中查詢(xún)最新的記錄行時(shí),通常我們會(huì)這么做:

$posts = Post::latest()->get();
// or $posts = Post::orderBy('created_at', 'desc')->get();

上面的查詢(xún)方式將會(huì)產(chǎn)生如下 sql 語(yǔ)句:

select * from posts order by created_at desc

這種查詢(xún)方式基本上都是按照 created_at 字段做降序排列來(lái)給查詢(xún)結(jié)果排序的。由于 created_at 字段是字符串類(lèi)型的數(shù)據(jù),所以用這種方式對(duì)查詢(xún)結(jié)果進(jìn)行排序通常會(huì)更慢。(譯者注:MySQL 的 TIMESTAMP 類(lèi)型字段是以 UTC 格式存儲(chǔ)數(shù)據(jù)的,形如 20210607T152000Z,所以 created_at 字段確實(shí)是字符串類(lèi)型的數(shù)據(jù))。

如果你的數(shù)據(jù)表中使用了自增長(zhǎng)的 id 字段作為主鍵,那么大多數(shù)情況下,最新的數(shù)據(jù)記錄行的 id 字段值也是最大的。因?yàn)?id 字段不僅是一個(gè)整形數(shù)據(jù)的字段,而且也是一個(gè)主鍵字段,所以基于 id 字段對(duì)查詢(xún)結(jié)果進(jìn)行排序會(huì)更快。所以查詢(xún)最新記錄的最佳實(shí)踐如下:

$posts = Post::latest('id')->get();
// or $posts = Post::orderBy('id', 'desc')->get();

該方法會(huì)產(chǎn)生如下 sql 語(yǔ)句

select * from posts order by id desc

17. 優(yōu)化 MySQL 的數(shù)據(jù)插入操作

為了更快地從數(shù)據(jù)庫(kù)查詢(xún)數(shù)據(jù),我們已經(jīng)為 select 方法做了很多優(yōu)化。 大多數(shù)情況下,我們只需要為查詢(xún)方法進(jìn)行優(yōu)化就可以滿足性能要求了。 但是很多時(shí)候我們還需要為『插入』和『更新』(insertupdate)方法進(jìn)行優(yōu)化。所以我給大家推薦一篇有趣的文章optimizing mysql inserts,這篇文章將有助于優(yōu)化緩慢的『插入』和『更新』操作。

18. 檢查和優(yōu)化查詢(xún)方法

在 Laravel 框架中,優(yōu)化數(shù)據(jù)查詢(xún)并沒(méi)有完全通用的辦法。你只能盡量搞清楚下面這些問(wèn)題:你的程序是如何運(yùn)行的、進(jìn)行了多少個(gè)數(shù)據(jù)庫(kù)查詢(xún)操作、有多少查詢(xún)操作是真正必要的。所以請(qǐng)檢查你的應(yīng)用產(chǎn)生的查詢(xún)操作,這將有助于你確定并減少數(shù)據(jù)查詢(xún)操作的總量。

有很多工具可以輔助你檢查每個(gè)頁(yè)面產(chǎn)生的查詢(xún)方法:

注意: 不推薦在生產(chǎn)環(huán)境下使用這些工具。在生產(chǎn)環(huán)境使用這些工具將會(huì)降低你的應(yīng)用性能,并且會(huì)讓未經(jīng)授權(quán)的用戶獲取到程序的敏感信息。

  • Laravel Debugbar - Laravel Debugbar 有一個(gè) database選項(xiàng)卡,點(diǎn)擊該選項(xiàng)卡將會(huì)展示你打開(kāi)一個(gè)頁(yè)面時(shí)應(yīng)用程序執(zhí)行的所有查詢(xún)語(yǔ)句。你可以瀏覽應(yīng)用的每個(gè)頁(yè)面并查看每個(gè)頁(yè)面用到的查詢(xún)語(yǔ)句。

  • Clockwork - Clockwork 與 Laravel Debugbar 一樣,只不過(guò) Clockwork 不會(huì)在你的網(wǎng)站上注入一個(gè)工具欄,你可以在『開(kāi)發(fā)者工具窗口』( developer tools window ),或者通過(guò)打開(kāi) url /yourappurl/clockwork 進(jìn)入一個(gè)單獨(dú)的頁(yè)面來(lái)查看應(yīng)用的調(diào)試信息。

  • Laravel Telescope - Laravel Telescope 是一個(gè)專(zhuān)為開(kāi)發(fā) Laravel 應(yīng)用而提供的十分優(yōu)秀的調(diào)試工具。一旦你安裝了 Laravel Telescope,便可以通過(guò)訪問(wèn) yourappurl/telescope 地址進(jìn)入它的儀表盤(pán)頁(yè)面。在 telescope 的儀表盤(pán)界面,點(diǎn)擊打開(kāi) queries 標(biāo)簽頁(yè),這個(gè)頁(yè)面將會(huì)展示你的應(yīng)用執(zhí)行過(guò)的所有 MySQL 查詢(xún)語(yǔ)句。

“Laravel8優(yōu)化數(shù)據(jù)庫(kù)查詢(xún)的技巧有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI