2016-12-28 46 views
2

我有這個例程從Web服務中獲取一些數據並將其存儲在我的數據庫中。這些數據有20k +項目。爲了將它們保存到數據庫中,我必須首先檢索一些信息然後存儲它們。所以我有這個運行20k +次的foreach循環,每次執行讀取和寫入數據庫。Laravel導入程序隨着時間推移減速

但是這種方法會隨着時間的推移而減慢。完成需要一個多小時!

我已經禁用了查詢日誌(DB::disableQueryLog()),但是我沒有注意到任何性能上的提升。

這裏是我的代碼:

$data = API::getItems(); 

foreach ($data as $item) { 
    $otherItem = OtherItem::where('something', $item['something'])->first(); 
    if (!is_null($otherItem)) { 
     Item::create([ 
      ... 
     ]); 
    } 
} 

作爲一個解決方案,我決定預取所有OtherItem到一個集合,它解決了這個問題:

$data = API::getItems(); 

$otherItems = OtherItem::all(); 

foreach ($data as $item) { 
    $otherItem = otherItems->where('something', $item['something'])->first(); 
    if (!is_null($otherItem)) { 
     Item::create([ 
      ... 
     ]); 
    } 
} 

但我想明白爲什麼第一種方法隨着時間的推移急劇減速,做這種事情的最好方法是什麼。

編輯:

澄清: 我知道,做20K的查詢是不是高性能和,在這種情況下,性能並不重要(除非它需要時間,而不是分鐘)。我只會在開發過程中運行這個例程。我最後的做法是兩種答案的混合(我沒有想過緩衝項目並將它們分批插入)。 下面是任何有興趣的代碼:

$data = collect(API::getPrices()); 
$chunks = $data->chunk(500); 

$otherItems = OtherItem::all(); 

foreach ($chunks as $items) { 
    $buffer = []; 
    foreach ($items as $item) { 
     $otherItem = otherItems->where('something', $item['something'])->first(); 
     if (!is_null($otherItem)) { 
      $buffer[] = [ 
       ... 
      ]; 
     } 
    } 
    Item::insert($buffer); 
} 

那麼,是什麼在困擾我的是,爲什麼是痛苦的緩慢(即使所有的查詢)。我決定做一些基準測試來進一步分析問題。 隨着兩個查詢方法,我得到如下結果:

對於6000循環:

  • 最大讀:11.5232小號
  • 民閱讀:0.0044小號
  • 平均閱讀:0.3196小號

  • 最大寫:0.9133 s

  • 最小寫:0.0007 s
  • 平均寫入:0.0085小號

每10-20 iteractions讀取時間上升到了2-3 iteractions這是一個怪異秒,我沒有IDEIA原因。

只是出於好奇,我也基準分塊和緩衝項之間的性差異插入DB之前:

  • 沒有緩衝:1 115,4秒(18分鐘35秒)
  • 分塊和緩衝:1064。7秒(17分45秒)
+0

您對where子句中使用的'something'字段有索引嗎? –

+0

不,我不知道。我會嘗試。 – flipjms

+0

如果在該字段中沒有索引,則搜索將花費的時間越長,添加到數據庫的項目越多。但無論如何,你的第二個解決方案要好得多。 1查詢與20k查詢。 – Pitchinnate

回答

0

因爲只有這麼多的查詢 - 每次都是往返數據庫,所以速度變慢。

你可以做的另一件事是嘗試分塊插入數據庫事務。玩確切的數字,但嘗試插入幾百個左右的批次。

  • 開始交易
  • 遍歷塊,執行插入
  • 提交
  • 重複用於下一個塊,直到沒有大塊保持

Laravel的ORM爲此提供了一個chunk method一種用例。

2

在第一個代碼片段中,您正在爲20000個項目創建40000個查詢。每個項目有兩個查詢 - 第一個將獲取數據,第二個將存儲數據。

第二個代碼片段將創建20001查詢,它也是非常慢的解決方案。

每次要存儲某些數據時,都可以構建數組並使用insert()而不是使用create()方法。因此,此代碼將只創建2個查詢,而不是40000和20001.

$otherItems = OtherItem::all(); 
$items = []; 

foreach ($data as $item) { 
    $otherItem = otherItems->where('something', $item['something'])->first(); 
    if (!is_null($model)) { 
     $items[] = [.....]; 
    } 
} 

Item::insert($items); 
+0

如果像這樣使用批量插入,請確保在創建數組時手動設置'created_at'和'updated_at',如果在表中包含它們。我不相信它會用這種方法自動設置它們。 – Pitchinnate

+0

@Pitchinnate它不會自動創建任何東西,所以是的,您應該手動將'created_at','updated_at','deleted_at'字段手動添加到數組。 –

相關問題