首頁  >  問答  >  主體

以 2k 最佳化 Laravel 的 foreach 循環,並從 200k 改進關係檢索以減少執行時間

我有一個名為「產品」的模型 我有一個名為 Note_voucher_line 的模型

這是產品內部的關係

public function get_note_voucher_lines()
{
    return $this->hasMany('App\Models\Note_voucher_line','product_id','id')->orderBy('date','asc')->orderBy('note_voucher_id','asc');
}

現在有時我必須循環像這樣的程式碼的產品

$products = Product::whereBetween('id',[$num1,$num2])->get();
foreach($products as $product)
{
    $lines = $product['get_note_voucher_lines'];
    // when i use this relation it tack long long time
}

模型 Note_voucher_line 擁有超過 300k 行 我在 migration 上有索引 這是note_voucher_lines內部的索引遷移

#
Schema::table('note_voucher_lines', function($table) {
    $table->foreign('note_voucher_id')->references('id')->on('note_vouchers');
    $table->foreign('user_id')->references('id')->on('users');
    $table->foreign('journal_entry_id')->references('id')->on('journal_entries');
    $table->foreign('warehouse_id')->references('id')->on('warehouses');
    $table->foreign('product_id')->references('id')->on('products');
    $table->foreign('cost_center_id')->references('id')->on('cost_centers');
    $table->foreign('unit_id')->references('id')->on('units');
    $table->foreign('is_it_bonus')->references('id')->on('status');
    $table->foreign('note_voucher_type_id')->references('id')->on('note_voucher_types');
    $table->foreign('posting_state_id')->references('id')->on('posting_status');
    $table->foreign('product_total_quantity_id')->references('id')->on('product_total_quantitys');
    $table->foreign('is_componentable')->references('id')->on('status');
    $table->foreign('approved_state_id')->references('id')->on('approval_status');
    $table->foreign('currency_id')->references('id')->on('currencies');
    $table->foreign('branch_id')->references('id')->on('branches');
    $table->foreign('created_by')->references('id')->on('users');
    $table->foreign('deleted_by')->references('id')->on('users');
});

產品表中有一個名為product_id的索引 這裡有任何幫助可以使它更快 謝謝

P粉707235568P粉707235568226 天前437

全部回覆(2)我來回復

  • P粉492959599

    P粉4929595992024-03-31 12:05:45

    在這種情況下,急切地加載你們的關係會很有幫助。

    $products = Product::whereBetween('id',[$num1,$num2])->with('get_note_voucher_lines')->get();
    foreach($products as $product)
    {
        $lines = $product->get_note_voucher_lines;
        // This should be faster and note that this is the right way to fetch laravel relation not as an array
    }

    這裡發生的情況是,我們使用with() 方法在每次迭代中從note_voucher_lines 表預先載入2k 行(假設您的產品表包含2k 行),而不是之前加載的一行。這減少了對資料庫伺服器進行的網路調用次數,現在不再進行 300k 次調用,而是進行 300k/2k 次調用。

    注意:您還應該考慮對您的產品使用區塊加載,以避免隨著資料的持續增長而達到記憶體限制。 https://laravel.com/docs/10.x/eloquent#chunking-結果

    回覆
    0
  • P粉244730625

    P粉2447306252024-03-31 09:24:23

    延遲的主要原因似乎是 get_note_voucher_lines 關係的延遲載入。

    每次您在循環內存取此關係時,Laravel 都會進行單獨的查詢以取得相關行。這就是所謂的 N 1 問題。

    為了緩解這種情況,請使用預先載入:

    $products = Product::whereBetween('id',[$num1,$num2])->with('get_note_voucher_lines')->get();

    您也可以使用分塊來處理大數據:

    Product::whereBetween('id',[$num1,$num2])->with('get_note_voucher_lines')->chunk(100, function ($products) {
    foreach ($products as $product) {
    
         $lines = $product->get_note_voucher_lines;
    }});

    確保 id 欄位上有索引。您提到有一個索引,但請確保它是一個正確的索引,而不僅僅是外鍵約束。

    回覆
    0
  • 取消回覆