簡體   English   中英

UPDATE查詢形式while循環,代碼很慢

[英]UPDATE query form while loop, code is very slow

我在Laravel App中有以下代碼:我讀取.csv文件中的每一行,並希望更新值。 但是,倍數的更新查詢使用8k行的.csv時非常慢。 我如何加快此代碼的速度? 謝謝

DB::beginTransaction();

        try {

            $delimiter = ",";
            $firstLine = true;

            if ($handle !== FALSE) {
                $position = 1;
                while (($csv_line = fgetcsv($handle, 1000, $delimiter)) !== FALSE) {

                    if ($firstLine == true) {
                        $firstLine = false;
                        continue;
                    }

                    $player_uid = $csv_line[0];

                    DB::table('scores')
                        ->where('season_uid', $season_uid)
                        ->where('day', $day)
                        ->where('player_uid', $player_uid)
                        ->update(['position' => $position]);

                    $position++;

                }
                fclose($handle);
            }

            DB::commit();
            return true;

        } catch (\Exception $e) {
            Log::error($e);
            DB::rollBack();
            return false;
        }

MySQL不支持大量更新,但是有一個巧妙的技巧可以使用ON DUPLICATE KEY UPDATE子句用插入替換更新。 這樣,您實際上可以批量更新記錄。 查看此答案以獲取一些示例

據我所知,盡管Laravel在其查詢生成器中不支持此子句,所以您將必須手動生成查詢並通過DB::statement()發出查詢。 確保對傳入的行進行分塊(例如,按100進行分組),您將看到速度顯着提高。

但是請務必意識到,更新8k行並不是一個便宜的操作。 最佳實踐是將其委派給單獨的作業,並在應用程序中設置隊列,以便工作人員可以在后台分別處理這些更新。 您可以在官方文檔中了解有關作業和隊列的更多信息。

確實建議使用單獨的作業來執行此操作,但是您可以嘗試以下代碼。 https://github.com/laravel/ideas/issues/575上找到了創建單個更新查詢的想法。 這個家伙減少了加載時間,最終快了13倍。

請注意,它之前沒有經過測試。

DB::beginTransaction();

try {
    $csv = array_map('str_getcsv', file('data.csv'));

    // remove the first line
    array_shift($csv);

    // grab only the players uids and their positions
    $positions = array_flip(array_column($csv, 0));

    array_walk($positions, static function(&$position, $id) {
        $position = "WHEN {$id} THEN {$position}";
    });

    DB::update("UPDATE `scores` 
                SET `position` = CASE `player_uid` " . implode(' ', $positions) . " END 
                WHERE `player_uid` in (" . implode(',', array_keys($positions)) . ") 
                  AND `session_uid` = ? 
                  AND `day` = ?", [$season_uid, $day]);

    DB::commit();

    return true;

} catch (\Exception $e) {
    Log::error($e);
    DB::rollBack();

    return false;
}

PS:最好用這種方法撰寫有關性能變化的評論

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM