0
0
Lập trình
Thaycacac
Thaycacac thaycacac

Xử lý Dữ liệu Lớn: Tối ưu Lumen & MySQL cho 300K Bản Ghi/ngày

Đăng vào 2 tuần trước

• 6 phút đọc

Xử lý Dữ liệu Lớn: Tối ưu Lumen & MySQL cho 300K Bản Ghi/ngày

Giới thiệu

Trong thời đại số ngày nay, việc xử lý hàng triệu bản ghi mỗi ngày đã trở thành một thử thách lớn đối với nhiều ứng dụng. Chúng ta cần những giải pháp hiệu quả để đảm bảo hệ thống hoạt động mượt mà ngay cả khi gặp tải cao. Trong bài viết này, chúng ta sẽ khám phá cách tối ưu hóa ứng dụng Lumen và MySQL để xử lý hơn 300.000 bản ghi mỗi ngày mà vẫn duy trì hiệu suất cao.

Thách thức

Hệ thống giám sát của chúng tôi yêu cầu xử lý hơn 300.000 bản ghi dữ liệu mỗi ngày, tạo ra các báo cáo và xuất dữ liệu phức tạp trong khi vẫn giữ thời gian phản hồi dưới một giây. Điều này bao gồm mọi thứ từ tổng hợp theo thời gian thực đến xuất dữ liệu CSV lớn, mọi thứ đều phải kiểm soát tốt về mức sử dụng bộ nhớ.

Chiến lược 1: Tối ưu hóa Sơ đồ Cơ sở Dữ liệu

Sử dụng Cột JSON với Cột Ảo

Thay vì tạo ra nhiều bảng với các phép nối phức tạp, chúng tôi đã tận dụng khả năng của MySQL với cột JSON:

php Copy
// Migration: Tạo cột ảo cho các trường JSON thường xuyên truy vấn
Schema::table('data_xxx', function (Blueprint $table) {
    $table->string('feedback_extracted')->virtualAs(
        "JSON_UNQUOTE(JSON_EXTRACT(content_data, '$.feedback'))"
    )->index();

    $table->decimal('amount_extracted', 15, 2)->virtualAs(
        "CAST(JSON_EXTRACT(content_data, '$.amount') AS DECIMAL(15,2))"
    )->index();
});

Tại sao điều này hiệu quả:

  • Cột ảo được tính toán ngay lập tức nhưng có thể được lập chỉ mục.
  • Loại bỏ nhu cầu về các phép nối phức tạp.
  • Duy trì tính linh hoạt của dữ liệu trong khi cho phép truy vấn nhanh.

Lập chỉ mục Chiến lược

php Copy
// Chỉ mục tổng hợp cho các mẫu truy vấn phổ biến
Schema::table('data_xxx', function (Blueprint $table) {
    $table->index(['branch', 'visit_date', 'visit_type']);
    $table->index(['personnel_id', 'visit_date', 'status']);
    $table->index(['visit_type', 'status', 'feedback_extracted']);
});

Chiến lược 2: Tối ưu hóa Truy vấn

Tránh N+1 với Tổng hợp Thông minh

Thay vì tải các quan hệ, chúng tôi thực hiện tổng hợp ở cấp độ cơ sở dữ liệu:

php Copy
public function getDataSummary($filters)
{
    return DB::table('data_xxx')
        ->select([
            'branch',
            DB::raw('SUM(CASE WHEN status = "COMPLETED" THEN 1 ELSE 0 END) as completed'),
            DB::raw('SUM(CASE WHEN status = "PLANNED" THEN 1 ELSE 0 END) as planned'),
            DB::raw('AVG(CAST(JSON_EXTRACT(content_data, "$.score") AS DECIMAL)) as avg_score')
        ])
        ->where('visit_date', $filters['date'])
        ->groupBy('branch')
        ->get();
}

Xử lý Dữ liệu với PHP Generators

Đối với các tập dữ liệu lớn, chúng tôi sử dụng PHP generators để duy trì mức sử dụng bộ nhớ ổn định:

php Copy
public function processLargeDataset($filters): \Generator
{
    $query = DB::table('data_xxx')
        ->where('visit_date', '>=', $filters['start_date'])
        ->where('visit_date', '<=', $filters['end_date'])
        ->orderBy('id');

    foreach ($query->lazy(2000) as $record) {
        yield $this->transformRecord($record);
    }
}

// Sử dụng
foreach ($this->processLargeDataset($filters) as $processedRecord) {
    // Mức sử dụng bộ nhớ không thay đổi bất kể kích thước tập dữ liệu
    $this->handleRecord($processedRecord);
}

Chiến lược 3: Hệ thống Xuất Dữ liệu Hiệu Suất Cao

Tạo CSV Tiết Kiệm Bộ Nhớ

Hệ thống xuất của chúng tôi xử lý các tập dữ liệu lớn trong khi giữ mức sử dụng bộ nhớ dưới 50MB:

php Copy
public function exportToCSV($filters): string
{
    // Tạo tệp tạm thời
    $tempFile = tmpfile();
    $tempPath = stream_get_meta_data($tempFile)['uri'];

    // Ghi tiêu đề
    fputcsv($tempFile, ['Date', 'Branch', 'Personnel', 'Customer', 'Result']);

    // Dữ liệu theo lô
    foreach ($this->getExportData($filters) as $record) {
        fputcsv($tempFile, [
            $record['visit_date'],
            $record['branch_name'],
            $record['personnel_name'],
            $record['customer_name'],
            $record['visit_result']
        ]);
    }

    // Tải lên kho lưu trữ
    $finalPath = "exports/data_" . date('Y-m-d_H-i-s') . ".csv";
    Storage::put($finalPath, fopen($tempPath, 'r'));

    fclose($tempFile);
    return $finalPath;
}

private function getExportData($filters): \Generator
{
    $query = DB::table('data_xxx')
        ->select([
            'visit_date', 'branch_name', 'personnel_name', 
            'customer_name', 'visit_result'
        ])
        ->where('visit_date', $filters['date'])
        ->orderBy('id');

    foreach ($query->lazy(2000) as $record) {
        yield (array) $record;
    }
}

Xử lý Nền với Các Hoạt Động Chia Lô

Đối với các hoạt động tốn thời gian, chúng tôi sử dụng hàng đợi công việc với chiến lược chia lô thông minh:

php Copy
public function processInBackground($requestData)
{
    // Tạo bản ghi theo dõi
    $exportLog = $this->createExportLog($requestData);

    // Đưa vào hàng đợi công việc
    Queue::push(new ProcessDataExport($exportLog->id, $requestData));

    return $exportLog;
}

// Trong lớp công việc
public function handle()
{
    $startTime = microtime(true);

    foreach ($this->getDataInChunks() as $chunk) {
        $this->processChunk($chunk);

        // Ngăn ngừa rò rỉ bộ nhớ và thời gian chờ
        if (microtime(true) - $startTime > 300) { // 5 phút
            Queue::push(new ProcessDataExport($this->logId, $this->remainingData));
            return;
        }
    }

    $this->markAsCompleted();
}

Chiến lược 4: Bộ Nhớ Cache và Tối Ưu

Xóa Bộ Nhớ Thông Minh

php Copy
public function getCachedSummary($filters)
{
    $cacheKey = 'summary_' . md5(serialize($filters));

    // Đối với dữ liệu hôm nay, cache trong 30 phút
    // Đối với dữ liệu lịch sử, cache trong 24 giờ
    $ttl = $filters['date'] === date('Y-m-d') ? 1800 : 86400;

    return Cache::remember($cacheKey, $ttl, function () use ($filters) {
        return $this->generateSummary($filters);
    });
}

Kết quả Hiệu Suất

Trước khi tối ưu:

  • Mức sử dụng bộ nhớ: 500MB+ cho các xuất dữ liệu lớn.
  • Thời gian xuất: 5+ phút cho 100K bản ghi.
  • CPU cơ sở dữ liệu: 80%+ trong giờ cao điểm.

Sau khi tối ưu:

  • Mức sử dụng bộ nhớ: <50MB liên tục.
  • Thời gian xuất: 30 giây cho 100K bản ghi.
  • CPU cơ sở dữ liệu: <30% trong giờ cao điểm.
  • Thời gian phản hồi: <200ms cho hầu hết các truy vấn.

Những điểm quan trọng

  1. Cột JSON + chỉ mục ảo loại bỏ các phép nối phức tạp trong khi duy trì hiệu suất truy vấn.
  2. PHP generators giữ mức sử dụng bộ nhớ ổn định bất kể kích thước tập dữ liệu.
  3. Chia lô chiến lược ngăn ngừa thời gian chờ và tiêu tốn tài nguyên.
  4. Chiến lược lập chỉ mục hợp lý là rất quan trọng cho các hoạt động khối lượng lớn.
  5. Xử lý theo luồng tốt hơn việc tải mọi thứ vào bộ nhớ.

Sự tuyệt vời của phương pháp này chính là sự đơn giản - không có công nghệ phức tạp, không có cơ sở dữ liệu kỳ lạ, chỉ là PHP và MySQL được tối ưu hóa tốt nhất để thực hiện điều mà chúng làm tốt nhất.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào