Sử dụng Lazy Collection để export 100,000 bản ghi
Lazy Collection là gì
Lazy Collection được giới thiệu bới Joseph Silber trong Laravel 6.0. Lazy collection là một class cho phép chúng ta duyệt các phần tử trong mảng. Layzy collection sửa dụng PHP generator để cho phép chúng ta làm việc với tập dữ liệu rất lớn và giữ cho mức sử dụng bộ nhớ ở mức thấp.
Bài toán đặt ra
Giá sử rằng CSDL của chúng tao có bảng chứa 100,000 bản ghi và chúng ta muốn lấy tất cả dữ liệu này, có thể xuất ra csv hoặc hiển thị lên cho người dùng
Nếu bạ sử dụng hàm all() trong Laravel Eloquent, hầu hết bộ nhớ của server sẽ được sử dụng để thực hiện truy vấn này. Việc truy vấn này sẽ làm 2 phần : đầu tiên là nạp tất các bản ghi được lấy ra từ CSDL, sau đó lấy đẩy bản ghi này vào collection. Với 100,000 bản ghi việc này sẽ ảnh hưởng đến hệ thống, vì ngoài chức năng này ra hệ thống của chúng ta cũng còn phải thực hiện nhiều chức năng khác (search, insert, update v...v..)
Thật may Laravel 6.0 đã cung cấp cho chúng ta Laravel Collection đễ làm việc với tập dữ liệu lớn với memory sử dụng thấp. Vậy chúng ta cùng bắt đầu nào
Cài đặt môi trường
1. Install composer
php composer-setup.php --install-dir=bin --filename=composer
mv composer.phar /usr/local/bin/composer
2. Install laravel
composer global require laravel/installer
3. Creata new project
laravel new test-lazy-collection
4. Make database
Cài đặt biến DB_DATABASE, DB_USERNAME, DB_PASSWORD trong file .env
5. Tạo seeder data
php artisan make:seeder UsersTableSeeder
Tạo random data
public function run()
{
DB::table('users')->truncate();
for($i=0; $i<=100; $i++){
factory(App\User::class, 1000)->create();
}
}
Update factory data
$factory->define(App\User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => bcrypt('123456'), // password
'remember_token' => Str::random(10),
];
});
6. Run seed data
php artisan db:seed
Tạo logic code xử lý export dữ liệu
1. Tạo routes
Route::get('/write', 'UsersController@write');
Route::get('/lazyWrite', 'UsersController@lazyWrite');
2. Thêm method checkDirOrMake() trong UserController
private function checkDirOrMake($dirname){
$filename = $dirname . "/";
if (!file_exists($filename)) {
mkdir($dirname, 0777);
}
}
Hàm checkDirOrMake() sẽ kiểm tra và tạo folder nếu nó có tồn tại hay không
3. write() function để export dữ liệu ra csv
public function write(){
$directory = storage_path("uploads");
$this->checkDirOrMake($directory);
$file = fopen($directory."/contacts.csv","w+");
$users = \App\User::all();
foreach($users as $user) {
$user = $user->toArray();
fputcsv($file, $user);
}
fclose($file);
}
4. lazyWrite() để export dữ liệu dùng Lazy Collection
public function lazyWrite(){
$directory = storage_path("uploads");
$this->checkDirOrMake($directory);
$file = fopen($directory."/lazy_contacts.csv","w+");
$users = \App\User::cursor()
->each(function ($user) use ($file) {
$user = $user->toArray();
fputcsv($file, $user);
});
fclose($file);
}
Thay vì sử dụng all() chúng ta sẽ sử dụng method cursor()
5. Run virtual server
php artisan serve
6. Chạy exoprt data
Export bình thường
http://localhost:8000/write
Export sử dụng lazy collection
http://localhost:8000/lazyWrite
7. Kết quả
Khi sử dụng export thông thường memory sẽ tốn rất nhiều trong khi sử dụng http://localhost:8000/lazyWrite việc export 100,000 bản ghi rất dễ dàng và tốn ít memory hơn rất nhiều
Kết luận
Việc sử dụng Lazy Collection sẽ tăng performance cho việc xử lý các tập kết quả lớn, nhưng sẽ là không tối ưu khi làm việc với tập dữ liệu nhỏ. Sử dụng Lazy Collection sẽ tùy vào bài toán cụ thể.
All rights reserved