Ví dụ: giả sử chúng tôi đã thiết kế một phần mềm kiểm kê và phần mềm này đang được sử dụng trong sản xuất trong một năm và đạt 1.000.000 giao dịch
Đột nhiên, chúng tôi biết rằng chúng tôi đã không thêm VAT vào các giao dịch của mình. Đối với các giao dịch trong tương lai, nó khá dễ xử lý, có thể với một công cụ biến đổi
class Transaction extends Model {
public $vat = 0.20;
public function setPriceAttribute[$value] {
$this->attributes['price'] += $value * $this->vat;
}
}
Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Hồ sơ trong tương lai là khá dễ dàng để đối phó với. Tuy nhiên, làm thế nào chúng ta sẽ chỉnh sửa 1 triệu bản ghi từ quá khứ
Để chỉnh sửa dữ liệu từ quá khứ, tôi thích tạo Seeder hơn
php artisan make:seeder AddVatToTransactions
Làm thế nào để không làm điều đó?
class AddVatToTransactions extends Seeder {
public function run[]
{
$vat = 0.20;
$transactions = Transaction::get[];
foreach [$transactions as $transaction] {
$transaction->price += $transaction->price * $vat
$transaction->save[];
}
}
}
Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Tuy nhiên, chạy nó trong một vòng lặp 1 triệu và thực hiện một “giao dịch cơ sở dữ liệu” trong mỗi lần lặp của vòng lặp - không phải là một ý tưởng hay. [Cảnh báo spoiler. Nó sẽ đóng băng hệ thống của bạn 😀]
Sớm muộn gì bạn cũng sẽ rơi vào tình huống cần cập nhật nhiều bản ghi cơ sở dữ liệu với các giá trị khác nhau. Laravel hỗ trợ cập nhật hàng loạt khi chỉ có một khả năng giá trị cho cột đã cho. Trong tài liệu chúng ta có thể tìm thấy ví dụ này
Flight::where['active', 1]
->where['destination', 'San Diego']
->update[['delayed' => 1]];
Điều này sẽ đặt thuộc tính bị trì hoãn thành đúng cho tất cả các chuyến bay đang hoạt động và có điểm đến là San Diego. Trong trường hợp này, điều này hoàn toàn ổn, nó sẽ chỉ sử dụng một truy vấn và nó sẽ khá nhanh. Nhưng nếu chúng ta muốn cập nhật nhiều bản ghi với các giá trị khác nhau thì sao? . Một số người dùng có 10 điểm, một số 20, v.v. Sẽ có một truy vấn cho mọi người dùng mà chúng tôi muốn cập nhật
Nếu chúng tôi phải cập nhật 5000 bản ghi, chúng tôi sẽ cần sử dụng vòng lặp
// $ids = array of random user ids0 và thực hiện 5000 truy vấn. Tôi đã cố gắng mô phỏng tình huống này và so sánh xem sẽ mất bao nhiêu thời gian để hoàn thành công việc. Vì vậy, tôi đã tạo cơ sở dữ liệu với 100 000 người dùng, chuẩn bị dữ liệu để cập nhật và chạy nó bên trong vòng lặp
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
// $ids = array of random user ids0
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
// $ids = array of random user ids
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
Trung bình mất 5. 47676 giây để cập nhật dữ liệu
Chúng tôi có thể bao bọc giao dịch nội bộ này và nó sẽ có tác động lớn đến hiệu suất
DB::transaction[function[] use[$ids, $data] {
for [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
}];
Chúng ta đang nói về 1. Trung bình 68872 giây. Điều này tốt hơn nhiều nhưng chúng ta có thể cải thiện điều này hơn nữa không?
Hãy thử thực hiện công việc tương tự bằng cách sử dụng gói cập nhật hàng loạt cho Laravel
use Mavinoo\Batch\Batch;
use App\Models\User;/* $updateData is array with length of 5000 and structure like this:
$updateData = [
[’id' => 100, 'points' => 200],
[’id' => 500, 'points' => 300] ...
]
*/Batch::update[new User, $updateData, 'id'];
Tham số đầu tiên là mô hình chúng tôi muốn cập nhật, tham số thứ hai là dữ liệu và tham số thứ ba là cột xác định duy nhất các bản ghi trong bảng được liên kết
Điều này chỉ mất 1. 0531 giây trung bình và một truy vấn duy nhất. Điều này đã được thực hiện bằng chức năng CASE của MySql
Bây giờ, hãy kiểm tra gói này và xem phương thức cập nhật hoạt động như thế nào bằng cách tập trung vào các phần quan trọng nhất của mã
Hãy tưởng tượng rằng phần tử đầu tiên của
// $ids = array of random user ids2 là mảng này
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}// $ids = array of random user ids3
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
Chúng tôi có hai vòng lặp foreach lồng nhau. Cái đầu tiên sẽ lặp lại trên// $ids = array of random user ids2 của chúng ta, mảng gồm 5000 mảng. Nó sẽ trích xuất giá trị của 'id' từ mảng đầu tiên và đẩy nó vào bên trong
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}// $ids = array of random user ids5array
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}// $ids = array of random user ids6 sẽ bằng 100
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
$finalField = $raw ? Common::mysql_escape[$val[$field]]: "'" . Common::mysql_escape[$val[$field]] . "'";
$value = [is_null[$val[$field]] ? 'NULL' :
$final[$field][] = 'WHEN `' . $index . '` = \'' . $val[$index] . '\' THEN ' . $value . ' ';
Vòng lặp tiếp theo sẽ trích xuất các trường cần được cập nhật. Trong trường hợp của chúng tôi, giá trị của trường $ sẽ là chuỗi 'điểm' và giá trị của
// $ids = array of random user ids7 sẽ là '230'. Chúng tôi an toàn trước SQL injection vì các biến này được truyền qua hàm
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
// $ids = array of random user ids8
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
Bước cuối cùng là tạo mảng có tên của cột cần được cập nhật làm khóa và chuỗi đại diện cho câu lệnh đã chuẩn bị cho hàm CASE của MySql làm giá trị. Sau đó, chúng tôi đẩy mảng đó vào bên trong biến
// $ids = array of random user ids9
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
Vì vậy, sau khi thực hiện vòng lặp
// $ids = array of random user ids0 thứ hai, mảng
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
// $ids = array of random user ids9 sẽ trông như thế này
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
array:1 [
"points" => array:1 [
0 => "WHEN `id` = '100' THEN '230' "
]
]
Chúng tôi chỉ cập nhật cột 'điểm' nhưng nếu chúng tôi cũng cập nhật một số cột khác, nó cũng sẽ thêm chúng
array:1 [
"points" => array:1 [
0 => "WHEN `id` = '100' THEN '230' "
],
"something" => array:1 [
0 => "WHEN `id` = '100' THEN 'some_value' "
]
]
Khi cả hai vòng lặp được thực hiện, mảng
DB::transaction[function[] use[$ids, $data] {
for [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
}];
2 sẽ bao gồm 5000 chuỗi giống như chuỗi ở trênCuối cùng, nó sẽ tạo một chuỗi truy vấn bằng cách kết hợp các giá trị từ các mảng
// $ids = array of random user ids9và
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}
// $ids = array of random user ids5
// $data = array of random numbers for pointsfor [$i = 0; $i $ids[$i]]]
->update[['points' => $data[$i]]];
}