Quay lại

Các cách để tối ưu hóa truy vấn cơ sở dữ liệu trong laravel Chuyên mục PHP và Laravel    2023-04-07    1.4k Lượt xem    128 Lượt thích    comment-3 Created with Sketch Beta. 0 Bình luận

Các cách để tối ưu hóa truy vấn cơ sở dữ liệu trong laravel

Hello xin chào tất cả mọi người hôm nay mình cùng nhau tìm hiểu những cách để optimize (tối ưu hóa) những truy vấn trong laravel nhé !

Nếu ứng dụng của bạn đang chạy chậm hoặc thực hiện nhiều truy vấn cơ sở dữ liệu, hãy làm theo các cách tối ưu hóa hiệu suất bên dưới để cải thiện thời gian tải ứng dụng của bạn nhé.

Dưới đây là các cách để  giúp các bạn tối ưu hóa về mysql, eloquent and raw database queries, let goooo !

1. Truy xuất 1 lượng dữ liệu lớn

Cách này chủ yếu tập trung vào việc cải thiện mức sử dụng bộ nhớ của ứng dụng khi xử lý các tập dữ liệu lớn.

Nếu ứng dụng của bạn cần xử lý một tập hợp lớn các bản ghi, thay vì truy xuất tất cả cùng một lúc, bạn có thể truy xuất một tập hợp con các kết quả và xử lý chúng theo nhóm.

Ví dụ để truy xuất một lượng dữ liệu lớn từ một bảng có tên là posts, chúng ta thường làm như dưới đây.

$posts = Post::all(); // khi chúng ta sử dụng eloquent
$posts = DB::table('posts')->get(); // khi chúng ta sử dụng query builder
foreach ($posts as $post){
  // Process posts 
}

Ví dụ trên sẽ lấy tất cả các bản ghi từ bảng posts và xử lý chúng. Nếu bảng này có 1 triệu bản ghi thì sao? Chúng ta sẽ bị lỗi hết bộ nhớ (out of memory).

Để tránh các sự cố khi xử lý các tập dữ liệu lớn, chúng ta có thể truy xuất một tập hợp con các kết quả và xử lý chúng như bên dưới.

 Cách 1 : Sử dụng Chunk

// Khi sử dụng eloquent
$posts = Post::chunk(100, function($posts){
  foreach ($posts as $post){
    // Process posts
  }
});

// Khi sử dụng query builder
$posts = DB::table('posts')->chunk(100, function ($posts){
  foreach ($posts as $post){
    // Process posts
  }
});

Ví dụ trên truy xuất 100 bản ghi từ bảng posts và xử lý chúng, lần tiếp theo nó lại truy xuất 100 bản ghi khác và xử lý chúng. Quá trình lặp lại này sẽ tiếp tục cho đến khi tất cả các bản ghi được xử lý.

Nếu bạn cần xử lý hàng nghìn bản ghi Eloquent, hãy sử dụng lệnh chunk. Phương thức chunk sẽ truy xuất một "khối" các Eloquent model, đưa chúng vào một Closure nhất định để xử lý. Sử dụng phương thức chunk sẽ tiết kiệm bộ nhớ khi làm việc với các tập kết quả lớn.

Thông thường việc xử lý các tập dữ liệu lớn sẽ được thực hiện chạy background. Vì bạn có thể thực hiện nhiều truy vấn hơn khi chạy backgound để tránh hết bộ nhớ khi xử lý các tập dữ liệu lớn.

 Cách 2 : Sử dụng Cursor

// Khi sử dụng eloquent
foreach (Post::cursor() as $post){
  // Process a single post
}
//Khi sử dụng query builder
foreach (DB::table('posts')->cursor() as $post){
 // Process a single post
}

Phương thức con trỏ (Cursor) cho phép bạn lặp qua các bản ghi cơ sở dữ liệu của mình bằng con trỏ, con trỏ này sẽ chỉ thực hiện một truy vấn duy nhất ( single query). Khi xử lý một lượng lớn dữ liệu, phương pháp con trỏ có thể được sử dụng để giảm đáng kể mức sử dụng bộ nhớ của bạn.

Con trỏ sử dụng PHP Generators, bạn có thể đọc ở php generators

 Cách 3 : Sử dụng ChunkById

// Khi sử dụng eloquent
$posts = Post::chunkById(100, function($posts){
  foreach ($posts as $post){
    // Process posts
  }
});

// Khi sử dụng query builder
$posts = DB::table('posts')->chunkById(100, function ($posts){
  foreach ($posts as $post){
    // Process posts
  }
});

Sự khác biệt chính giữa chunkchunkById là chunk truy xuất dựa trên offset và limit.

Trong khi chunkById truy xuất kết quả cơ sở dữ liệu dựa trên trường id. Trường id này thường là trường số nguyên và trong hầu hết các trường hợp, trường này sẽ là trường tăng tự động.

Các truy vấn được thực hiện bởi chunk và chunkById như sau:

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

Nói chung, sử dụng litmit với offset sẽ chậm hơn và chúng ta nên cố gắng tránh sử dụng nó. Bài viết này giải thích chi tiết về vấn đề sử dụng offset.

Với chunkById đang sử dụng trường id là một số nguyên và truy vấn đang sử dụng mệnh đề where nên truy vấn sẽ nhanh hơn nhiều.

khi nào bạn có thể sử dụng chunkById?

- Nếu bảng cơ sở dữ liệu của bạn có một primary key column và là incrementing field.

2. Chỉ chọn các columns bạn cần

Thông thường để lấy kết quả từ một bảng cơ sở dữ liệu, chúng ta sẽ làm như sau.

$posts = Post::find(1); // Khi sử dụng eloquent
$posts = DB::table('posts')->where('id','=',1)->first(); // Khi sử dụng query builder

Đoạn mã trên sẽ dẫn đến một truy vấn như dưới đây:

select * from posts where id = 1 limit 1

Như bạn có thể thấy, truy vấn đang thực hiện thao tác chọn *. Điều này có nghĩa là nó đang truy xuất tất cả các cột từ bảng cơ sở dữ liệu. Điều này chỉ tốt khi chúng ta lấy hết tất cả các cột trong bảng.

Thay vào đó, nếu chúng ta chỉ cần các cột cụ thể (id, title), chúng ta chỉ có thể truy xuất các cột đó như bên dưới:

$posts = Post::select(['id','title'])->find(1); //Khi sử dụng eloquent
$posts = DB::table('posts')->where('id','=',1)->select(['id','title'])->first(); //Khi sử dụng query builder

Đoạn mã trên sẽ dẫn đến một truy vấn như dưới đây:

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

3. Sử dụng Pluck khi bạn cần chính xác một hoặc hai columns từ database

Như tôi đã đề cập ở trên, để truy xuất các cột cụ thể, chúng tôi sẽ thực hiện

$posts = Post::select(['title','slug'])->get(); //Khi sử dụng eloquent
$posts = DB::table('posts')->select(['title','slug'])->get(); //Khi sử dung query builder

Vậy đoạn code trên thực thi như thế nào ? đây là các bước mà nó thực thi :

  • Executes select title, slug from posts query on the database
  • Tạo mới một Post object cho mỗi bản ghi mà nó đã truy xuất (Đối với query builder, nó tạo một đối tượng tiêu chuẩn PHP)
  • Tạo một collection mới với các Post Models
  • Trả về collection

Bây giờ, để truy cập kết quả, chúng ta sẽ làm:

foreach ($posts as $post){
  // $post is a Post model or php standard object
  $post->title;
  $post->slug;
}

Điều này sẽ là tốt nhất nếu bạn thực sự cần Post Model instance thay vì data.

Nhưng nếu tất cả những gì bạn cần là hai giá trị đó, bạn có thể làm như sau:

$posts = Post::pluck('title', 'slug'); // Khi sử dụng eloquent
$posts = DB::table('posts')->pluck('title','slug'); //Khi sử dụng query builder

Đây là các bước mà nó thực thi :

  • Thực thi select title, slug from posts truy vấn trên cơ sở dữ liệu
  • Tạo một Array với title là giá trị mảng và slug là khóa mảng
  • Trả về mảng (định dạng mảng: [ slug => title, slug => title ])

Bây giờ, để truy cập kết quả :

foreach ($posts as $slug => $title){
   // $title is the title of a post
   // $slug is the slug of a post
}

Nếu bạn chỉ muốn lấy một cột, bạn có thể làm:

$posts = Post::pluck('title'); // Khi sử eloquent
$posts = DB::table('posts')->pluck('title'); //Khi sử dụng query builder
foreach ($posts as $title){
  // $title is the title of a post
}

Cách tiếp cận trên loại bỏ việc tạo các object Post cho mỗi bản ghi. Do đó giảm mức sử dụng bộ nhớ và thời gian dành cho việc xử lý kết quả truy vấn.

4. Count dữ liệu bằng query thay vì sử dụng collection

Để đếm tổng số bản ghi trong một bảng, thông thường chúng ta sẽ làm.

$posts = Post::all()->count(); //Khi sử dụng eloquent
$posts = DB::table('posts')->get()->count(); //Khi sử dụng query builder

Điều này sẽ tạo ra truy vấn sau:

select * from posts

Cách tiếp cận trên sẽ truy xuất tất cả các bản ghi từ database, sẽ đưa chúng vào collection object và đếm kết quả. Điều này hoạt động tốt khi có ít dữ liệu trong bảng cơ sở dữ liệu. Nhưng chúng ta sẽ nhanh chóng hết bộ nhớ khi bảng lớn lên.

Thay vì cách tiếp cận trên, chúng ta có thể đếm trực tiếp tổng số bản ghi trên chính cơ sở dữ liệu

$posts = Post::count(); //Khi sử dụng eloquent
$posts = DB::table('posts')->count(); //Khi sử dụng query builder

Điều này sẽ tạo ra truy vấn sau:

select count(*) from posts;

Đếm các bản ghi trong sql là một quá trình chậm và hoạt động rất kém khi bảng cơ sở dữ liệu có quá nhiều bản ghi. Tốt hơn là tránh đếm các bản ghi càng nhiều càng tốt.

5. Tránh truy vấn N+1 (eager loading relationship)

Chắc hẳn bạn cũng đã nghe về cách này nhiều lần. Vì vậy, tôi sẽ giữ nó ngắn gọn và đơn giản nhất có thể. Giả sử bạn có tình huống sau:

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

// posts/index.blade.php file
@foreach($posts as $post)
  <li>
    <h3>{{ $post->title }}</h3>
    <p>Author: {{ $post->author->name }}</p>
  </li>
@endforeach

Đoạn code trên đang truy xuất tất cả các bài posts và hiển thị tiêu đề của bài post cũng như author của nó trên trang web. Đoạn code trên chỉ ra rằng mối quan hệ của bảng author có trong post model.

Việc thực thi đoạn mã trên sẽ dẫn đến việc chạy các truy vấn sau.

select * from posts //Giả sử truy vấn này trả về 5 bài viết
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 }

Như bạn có thể thấy, chúng ta có một truy vấn để truy xuất bài đăng và 5 truy vấn để truy xuất tác giả của bài đăng (Vì chúng ta giả sử chúng ta chỉ có 5 bài đăng.) Vì vậy, đối với mỗi bài đăng được truy xuất, nó sẽ thực hiện một truy vấn riêng để truy xuất tác giả của nó.

Vì vậy, nếu có N số bài đăng, nó sẽ thực hiện N+1 truy vấn (1 truy vấn để truy xuất bài đăng và N truy vấn để truy xuất tác giả cho mỗi bài đăng). Điều này thường được gọi là vấn đề truy vấn N+1.

Để tránh điều này, eager loading mối quan hệ của author trên các bài posts như bên dưới.

$posts = Post::all(); // Tránh sử dụng kiểu này
$posts = Post::with(['author'])->get(); // Làm kiểu này để thay thế

Việc thực thi đoạn mã trên sẽ dẫn đến việc chạy các truy vấn sau.

select * from posts // Giả sử truy vấn này trả về 5 bài viết
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )

6. Eager loading nested relationship

Từ ví dụ trên, giả sử author thuộc về một team và bạn cũng muốn hiển thị tên nhóm. Vì vậy, trong blade, bạn sẽ làm như dưới đây

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

Bây giờ làm như dưới đây:

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

Sẽ dẫn đến các truy vấn sau:

select * from posts // Giả sử truy vấn này trả về 5 bài viết
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 }

Như bạn có thể thấy, mặc dù chúng ta eager loading author relationship, nhưng nó vẫn tạo ra nhiều truy vấn hơn. Bởi vì chúng ta không eager loading mối quan hệ của team lên các authors.

Chúng ta có thể khắc phục điều này bằng cách làm như sau:

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

Thực thi đoạn mã trên sẽ dẫn đến việc chạy các truy vấn sau:

select * from posts // Giả sử truy vấn này trả về 5 bài viết
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 } )

Vì vậy, bằng cách này, chúng ta đã giảm tổng số truy vấn từ 11 xuống còn 3.

7. Không load belongsTo relationship nếu bạn chỉ cần id của nó

Hãy tưởng tượng bạn có hai bảng posts và authors. Bảng post có một cột author_id đại diện cho mối quan hệ thuộc về trên bảng authors.

Để lấy id tác giả của một bài đăng, thông thường chúng ta sẽ làm:

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

Điều này sẽ dẫn đến hai truy vấn được thực hiện:

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

Thay vào đó, bạn có thể trực tiếp lấy id tác giả bằng cách thực hiện như sau:

$post = Post::findOrFail(<post id>);
$post->author_id; // bảng posts có một column author_id nơi lưu trữ id của tác giả

8. Tránh truy vấn không cần thiết

Thông thường, chúng ta thực hiện các truy vấn cơ sở dữ liệu không cần thiết. Hãy xem xét ví dụ dưới đây:

<?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 ]);
   }
}

Đoạn mã trên đang truy xuất dữ liệu từ hai bảng khác nhau (ví dụ: posts, private_posts) và hiện thị. View file sẽ như dưới đây:

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

Như bạn có thể thấy ở trên, $private_posts chỉ hiển thị với người dùng là quản trị viên. Phần còn lại tất cả người dùng không thể nhìn thấy những bài đăng này

Vấn đề ở đây là, khi chúng ta đang làm:

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

Chúng ta đang thực hiện hai truy vấn. Một để lấy các bản ghi từ bảng bài viết và một để lấy các bản ghi từ bảng private_posts.

Các bản ghi từ bảng private_posts chỉ hiển thị với người dùng quản trị. Nhưng chúng ta vẫn đang thực hiện truy vấn để truy xuất các bản ghi này cho tất cả người dùng mặc dù chúng không hiển thị.

Chúng ta có thể sửa đổi logic của mình thành bên dưới để tránh truy vấn thêm này.

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

Bằng cách thay đổi logic của chúng ta ở trên, chúng ta đang thực hiện hai truy vấn một là người dùng quản trị và truy vấn cho tất cả những người dùng khác.

9. Hợp nhất các truy vấn tương tự lại với nhau

Đôi khi, chúng ta cần thực hiện các truy vấn để truy xuất các loại bản dữ liệu khác nhau từ cùng một bảng

$published_posts = Post::where('status','=','published')->get();
$featured_posts = Post::where('status','=','featured')->get();
$scheduled_posts = Post::where('status','=','scheduled')->get();

Đoạn code trên đang truy xuất các bản ghi có trạng thái khác với cùng một bảng. Mã này sẽ dẫn đến việc thực hiện các truy vấn sau:

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

Như bạn có thể thấy, nó đang thực hiện 3 truy vấn khác nhau cho cùng một bảng để truy xuất các bản ghi. Chúng ta có thể cấu trúc lại đoạn code này để chỉ thực hiện một truy vấn cơ sở dữ liệu

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

Đoạn code trên đang thực hiện một truy vấn để truy xuất tất cả các bài posts có bất kỳ status nào được chỉ định và tạo các collection riêng cho từng trạng thái bằng cách lọc các bài posts được trả về theo trạng thái của chúng. Vì vậy, chúng ta vẫn sẽ có ba biến khác nhau với trạng thái của chúng và sẽ chỉ thực hiện một truy vấn.

10. Thêm index vào các cột được truy vấn thường xuyên

Nếu bạn đang thực hiện truy vấn bằng cách thêm where condition bằng một string và dựa trên column, thì tốt hơn là thêm index vào cột. Truy vấn nhanh hơn nhiều khi truy vấn các bản ghi có cột chỉ mục.

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

Trong ví dụ trên, chúng ta đang truy vấn bản ghi bằng cách thêm where condition vào status column. Chúng ta có thể cải thiện hiệu suất của truy vấn bằng cách thêm index vào database migration như sau:

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

11. Sử dụng SimplePaginate thay vì Paginate

Khi phân trang kết quả, chúng ta thường làm:

$posts = Post::paginate(20);

Điều này sẽ thực hiện 2 truy vấn. 1 để truy xuất kết quả được phân trang và một để đếm tổng số row trong bảng. Đếm các row trong bảng là một thao tác chậm và sẽ ảnh hưởng tiêu cực đến hiệu suất truy vấn.

Vậy tại sao laravel đếm tổng số bản ghi?

Để tạo các liên kết phân trang, Laravel đếm tổng số row. Vì vậy, khi các pagination links được tạo, bạn biết trước có bao nhiêu trang ở đósố trang trước đó là bao nhiêu. Vì vậy, bạn có thể điều hướng đến bất kỳ trang nào bạn muốn một cách dễ dàng.

Mặt khác, thực hiện SimplePaginate sẽ không tính tổng số bản ghi và truy vấn sẽ nhanh hơn nhiều so với cách tiếp cận paginate. Nhưng bạn sẽ mất khả năng biết số trang cuối cùngcó thể chuyển sang các trang khác nhau

Nếu bảng cơ sở dữ liệu của bạn có quá nhiều dữ liệu, tốt hơn hết là không nên dùng paginate và thay vào đó hãy thực hiện SimplePaginate.

$posts = Post::paginate(20); // Generates pagination links for all the pages
$posts = Post::simplePaginate(20); // Generates only next and previous pagination links

Khi nào nên sử dụng paginate và SimplePaginate?

paginate SimplePaginate
Bảng cơ sở dữ liệu chỉ có vài row và không phát triển lớn Bảng cơ sở dữ liệu có rất nhiều bản ghi và phát triển nhanh chóng

Bắt buộc phải cung cấp tùy chọn để người dùng chuyển đến các trang cụ thể.

Bắt buộc phải hiển thị cho người dùng tổng số kết quả

Không sử dụng khi có liên kết đến trang cụ thể

Sử dụng cho nút "load more" hoặc "infinite scrolling" để phân trang

12. Tránh sử dụng các ký tự đại diện đứng đầu (LIKE keyword)

Khi cố gắng truy vấn các kết quả khớp với một dữ liệu cụ thể nào đó, chúng ta thường sử dụng:

select * from table_name where column like %keyword%

Truy vấn trên sẽ dẫn đến việc quét toàn bộ dự liệu trong bảng. Nếu chúng ta biết từ khóa xuất hiện ở đầu giá trị cột, chúng ta có thể truy vấn kết quả như bên dưới

select * from table_name where column like keyword%

13. Tránh sử dụng các hàm SQL trong where clause

Luôn luôn tốt hơn để tránh các hàm SQL trong where clause vì chúng dẫn đến việc quét toàn bộ bảng. Hãy xem ví dụ dưới đây. Để truy vấn kết quả dựa trên ngày nhất định, chúng tôi thường làm

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

Điều này sẽ dẫn đến một truy vấn tương tự như dưới đây:

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

Truy vấn trên sẽ dẫn đến việc quét toàn bộ bảng.

Chúng ta có thể cấu trúc lại cái này như bên dưới:

$posts = Post::where('created_at', '>=', now() )->get();

Điều này sẽ dẫn đến một truy vấn tương tự như dưới đây:

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

14. Tránh thêm quá nhiều cột vào bảng

Tốt hơn là giới hạn tổng số cột trong một bảng. Cơ sở dữ liệu quan hệ như mysql, có thể được tận dụng để chia các bảng có nhiều cột thành nhiều bảng. Chúng có thể được nối với nhau bằng cách sử dụng khóa chính và khóa ngoại của chúng.

Việc thêm quá nhiều cột vào một bảng sẽ làm tăng độ dài bản ghi riêng lẻ và sẽ làm chậm quá trình quét bảng. Khi bạn đang thực hiện truy vấn select *, cuối cùng bạn sẽ truy xuất một loạt các cột mà bạn thực sự không cần.

15. Tách các cột có kiểu dữ liệu văn bản thành bảng riêng

Cách này là từ kinh nghiệm cá nhân và không phải là cách tiêu chuẩn để kiến ​​trúc các bảng cơ sở dữ liệu của bạn. Tôi khuyên bạn chỉ nên làm theo cách này nếu bảng của bạn có quá nhiều bản ghi hoặc sẽ phát triển nhanh chóng.

Nếu một bảng có các cột lưu trữ một lượng lớn dữ liệu (ví dụ: các cột có kiểu dữ liệu là TEXT), thì tốt hơn là tách chúng thành bảng riêng hoặc thành một bảng ít bị hỏi hơn.

Khi bảng có các cột chứa lượng lớn dữ liệu trong đó, kích thước của một bản ghi riêng lẻ sẽ tăng lên rất cao. Cá nhân tôi đã quan sát thấy nó ảnh hưởng đến thời gian truy vấn trên một trong các dự án của tôi.

Hãy xem xét trường hợp bạn có một bảng có tên là posts với một cột content lưu trữ nội dung bài đăng trên blog. Nội dung cho bài đăng trên blog sẽ thực sự rất lớn và đôi khi, bạn chỉ cần dữ liệu này nếu một người đang xem bài đăng trên blog cụ thể này.

Vì vậy, việc tách cột này khỏi bảng bài đăng sẽ cải thiện đáng kể hiệu suất truy vấn khi có quá nhiều bài đăng.

16. Cách tốt nhất để truy xuất các bản ghi mới nhất từ ​​​​một bảng

Khi chúng ta muốn truy xuất các bản ghi mới nhất từ ​​một bảng, chúng ta thường làm

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

Cách tiếp cận trên sẽ tạo ra truy vấn sql sau:

Truy vấn về cơ bản là sắp xếp các bản ghi theo thứ tự giảm dần dựa trên cột created_at. Vì cột created_at là cột dựa trên chuỗi nên việc sắp xếp kết quả theo cách này thường chậm hơn.

Nếu bảng cơ sở dữ liệu của bạn có id khóa chính tăng tự động, thì trong hầu hết các trường hợp, bản ghi mới nhất sẽ luôn có id cao nhất. Vì trường id là trường số nguyên và cũng là khóa chính nên việc sắp xếp kết quả dựa trên khóa này sẽ nhanh hơn nhiều. Vì vậy, cách tốt hơn để truy xuất các bản ghi mới nhất như sau:

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

Cách tiếp cận trên sẽ tạo ra truy vấn sql sau:

select * from posts order by id desc

17. Tối ưu hóa insert trong mysql

Chúng ta đã xem xét việc tối ưu hóa các truy vấn select để truy xuất kết quả từ cơ sở dữ liệu. Hầu hết các trường hợp chúng ta chỉ cần tối ưu hóa các truy vấn đọc. Nhưng đôi khi chúng tôi thấy cần phải tối ưu hóa các truy vấn insert và update. Tôi đã tìm thấy một bài viết thú vị về việc tối ưu hóa các phần insert vào mysql sẽ giúp tối ưu hóa các phần insert và update chậm.

18. Inspect and optimize queries

Không có một giải pháp chung nào khi tối ưu hóa các truy vấn trong laravel. Chỉ bạn mới biết ứng dụng của mình đang làm gì, nó đang thực hiện bao nhiêu truy vấn, bao nhiêu trong số đó đang thực sự được sử dụng. Vì vậy, việc kiểm tra các truy vấn được thực hiện bởi ứng dụng của bạn sẽ giúp bạn xác định và giảm tổng số truy vấn được thực hiện.

Có một số công cụ giúp bạn kiểm tra các truy vấn được thực hiện trên mỗi và mọi trang.

  • Laravel Debugbar: Laravel debugbar có một tab được gọi là database sẽ hiển thị tất cả các truy vấn được thực hiện khi bạn truy cập một trang. Truy cập tất cả các trang trong ứng dụng của bạn và xem các truy vấn được thực hiện trên mỗi trang.
  • Clockwork: Clockwork giống như laravel Debugbar. Nhưng thay vì đưa thanh công cụ vào trang web của bạn, nó sẽ hiển thị thông tin gỡ lỗi trong cửa sổ công cụ dành cho nhà phát triển hoặc dưới dạng giao diện người dùng độc lập bằng cách truy cập yourappurl/clockwork 
  • Laravel Telescope: Laravel Telescope là một người bạn đồng hành gỡ lỗi tuyệt vời trong khi phát triển các ứng dụng laravel tại local. Sau khi Laravel Telescope được cài đặt, bạn có thể truy cập bảng điều khiển bằng cách truy cập yourappurl/telescope của bạn. Trong bảng điều khiển của Laravel Telescope, hãy chuyển đến tab truy vấn và nó sẽ hiển thị tất cả các truy vấn đang được ứng dụng của bạn thực hiện. 

Kết luận

Như các bạn đã thấy có vô vàn cách để optimize query để giúp ứng dụng của bạn trở nên nhanh hơn và tiết kiệm tài nguyên hơn như 18 cách mình kể trên, các bạn có thể đọc và áp dụng nó vào chương trình của mình nhé , Xin cảm ơn

Bình luận (0)

Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough

Bài viết liên quan

Learning English Everyday