performance optimization

performance optimization

I. Database

  1. Eager loading

Một vấn đề khá phổ biến khi chúng ta phát triển ứng dụng bằng framework Laravel, đó là vấn đề truy vấn N + 1 query. Việc sử dụng Eloquent Relationships nếu không để ý hoặc chưa thực sự hiểu thì rất dễ xảy ra vấn đề này, dẫn đến thừa rất nhiều các câu truy vấn.

Vd:

$books = Books::all();
 
foreach ($books as $book) {
    echo $book->category->name;
}

Nếu chúng ta sử dụng code như trên kia về mặt kết quả hoàn toàn k sai. Tuy nhiên vòng lặp này sẽ thực hiện một truy vấn để truy xuất tất cả các books trong cơ sở dữ liệu, sau đó một truy vấn khác cho từng books để lấy ra tên thể loại sách. Để giải quyết bài toán này chúng ta sẽ sử dụng Eager Loading và mình sửa lại code một xíu ạ:

$books = Books::with('category')->get();
 
foreach ($books as $book) {
    return $book->category->name;
}

Bằng cách sử dụng eager loading thì giờ đây trong lúc truy vấn lấy ra tất cả books, chúng ta đã lấy ra tên thể loại của books. Với cách này ở bài toán trên chúng ta đã giảm còn 2 câu truy vấn.

2. Database index (indexing).

Index là gì? Index là một dạng cấu trúc dữ liệu, trong đó chứa giá trị của một trường nhất định trong một bảng dữ liệu, đồng thời trỏ đến row dữ liệu tương ứng. Việc đánh index cho các trường là rất cần thiết nó giúp tăng hiệu suất khi thu thập dữ liệu.

Do đó, khi thực hiện tối ưu hiệu năng nên đánh index cho các trường, ưu tiên theo nguyên tắc:

  1. Ưu tiên khóa chính (Trong Laravel khóa chính đã được đánh index)
  2. Khóa ngoại trên các bảng
  3. Các trường xuất hiện trong câu các câu truy vấn where

Lưu ý: Việc đánh index cho một số trường trong DB giúp cho việc select dữ liệu ra nhanh hơn, tuy nhiên nó sẽ khiến cho việc create và update chậm hơn so với việc chưa đánh index.

II. code

  1. in_Array()

Khi bạn gọi in_array($needle, $haystack), PHP sẽ duyệt qua từng phần tử của $haystack và so sánh nó với $needle. Nếu một phần tử trong $haystack khớp với $needle, hàm sẽ trả về true. Nếu không có phần tử nào khớp, hàm sẽ trả về false.

$exist_license_keys = [...]; // trên 200,000 item
        $create_license_keys = [];

        for ($i = 0; $i < $count; $i++) {
            while (true) {
                $license_key = Str::random(9);

                if (! in_array($license_key, $exist_license_keys) && ! in_array($license_key, $create_license_keys)) { // check tồn tại data mới tạo không có trong mảng mới và cũ
                    $create_license_keys[] = $license_key;
                    break;
                }
            }
        }
xử lí > 10s nếu $count > 10000

Đoạn code trên mục đích để tạo 1 mảng chứa các mã không trùng với mã cũ. Nếu $count nhỏ đoạn code trên không có ảnh hưởng gì nhưng khi $count lớn thời gian xử sẽ rất lâu  vì mỗi 1 vòng lặp cần kiểm tra giá trị 2 lần dùng in_array() .Thay vì viết như trên tạo trước tạo trước tất cả các mã và check trùng sau

$exist_license_keys = [...]; // trên 200,000 item
        $create_license_keys = [];

        for ($i = 0; $i < $count; $i++) {
            $create_license_keys[] = Str::random(9);
        }
        
        $new_array = array_diff(array_unique($create_license_keys), $exist_license_keys);
        $newCount = count($new_array);
        if($new_count == $count) {
        	return $new_array;
        } else {
            for ($i = 0; $i < $count - $new_count; $i++) {
                while (true) {
                    $license_key = Str::random(9);

                    if (! in_array($license_key, $exist_license_keys) && ! in_array($license_key, $new_array)) { // check tồn tại data mới tạo không có trong mảng mới và cũ
                        $new_array[] = $license_key;
                        break;
                    }
                }
            }
            return $new_array;
        }
        
        
       
xử lí ~ 1s

=> chúng ta cần chú ý hiểu rõ cách hoạt động của các hàm muốn dùng và yêu cầu của đầu vào/ đầu ra của hệ thống để lựa chọn đúng các hàm để xử lí tránh việc làm giảm hiểu suất của hệ thống.

2. Tối ưu các bước sử lí

    $users = User::get();
        $data = [];
        foreach ($users as $user) {
            $data[] = [
                'id' => $user->id,
                'name' => $user->name,
                'description' => $user->description_admin,
            ];

Thay Vì sử lí 2 bước ta có thể gộp lại thành 1 bước sau

    $users = User::select('id', 'name', 'description_admin as description')->get()->toArray();

= > làm gọn các bước thực thi tránh lòng vòng tốn thời gian thực hiện nhiều lần

......

Trên đây là một số tips mình từng dùng để cải thiện performance cho các dự án. Hy vọng sẽ giúp ích được cho mọi người ạ

Tài liệu tham khảo : https://viblo.asia/p/nhung-tips-toi-uu-hoa-performance-trong-ung-dung-laravel-aWj53BBGl6m