集合性能问题

这两天在修改以前的一个功能模块的时候,发现有一短代码执行效率非常低,正常只需要几毫秒就能执行完,却花了好几秒,
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 协议》,转载必须注明作者和本文链接
为了点个赞,专门注册的账号
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 9

个人感觉为了降低数据库压力使用集合得不偿失。反而丧失了 SQL 原本有的优势。个人观点。

5年前 评论
air93610 3年前

@Wi1dcard 是有这种感觉:joy:

5年前 评论

从数据库里面获取数据集合的时候就可以进行过滤,不知道题主为啥会需要提取出来再进行过滤

5年前 评论
banda 7个月前

@mojiajuzi 业务逻辑需要,不过这不是重点,讨论的重点在于 Collectionwhere() 方法的效率

5年前 评论

经过一番查找之后,发现了一篇对同样问题更专业的测试,添加到了附言中,有兴趣的也可以看看

5年前 评论

@mojiajuzi 我讲的也不是太好,:sweat_smile:

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!