集合效能問題

王成濤發表於2018-12-10

這兩天在修改以前的一個功能模組的時候,發現有一短程式碼執行效率非常低,正常只需要幾毫秒就能執行完,卻花了好幾秒,
file
一步步追蹤下去,發現問題出在集合查詢。

$this->countryReplenish->where('partsId', $asp->partsId)
            ->where('brandId', $asp->brandId)
            ->where('countryId', $asp->countryId)
            ->first();

$this->countryReplenish 是個大小為3W左右的集合。我這樣做的理由是為了減少訪問資料庫次數,從資料庫裡邊把需要用到的資料全部讀出來,以後要用的時候只需要用集合操作。用 microtime(true) 對這段程式碼計時,1.34秒:scream:
file
多試幾次,還是需要1秒多的時間
file
當把集合查詢改為資料庫查詢的時候,效率就很高了,幾毫秒就搞定
file
檢視一下 Collection where() 方法的原始碼

public function where($key, $operator, $value = null)
    {
       .
       .
       .

        return $this->filter($this->operatorForWhere($key, $operator, $value));
    }

檢視 operatorForWhere() 方法,返回一個閉包

protected function operatorForWhere($key, $operator, $value)
    {
        return function ($item) use ($key, $operator, $value) {
            $retrieved = data_get($item, $key);
            .
            .
            .
    }

其中 data_get() 函式包含一個 while 迴圈,時間複雜度為O(n)。
然後結合 filter() 方法過濾,返回一個新集合

public function filter(callable $callback = null)
    {
        if ($callback) {
            return new static(Arr::where($this->items, $callback));
        }
        return new static(array_filter($this->items));
    }

因此當呼叫多個where() 方法時,時間複雜度為O(n m k)。

按照這樣的思路,嘗試只呼叫一次 where() 方法,尷尬的是,還是花了1秒多,效率和多次呼叫 where() 方法差不多。

那麼問題來了,難道集合操作的效率真的很低嗎?
經過查詢資料,答案就在附言中。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
為了點個贊,專門註冊的賬號

相關文章