上一篇文章在最后提到“在foreach循环中嵌套查询,这是非常耗费资源的”。当时考虑到本站应用场景简单,查询仅为百位数量级,性能损失忽略不计,就图简单没有深入去重写程序。今天得空,改造了下程序,通过操作数组来达到避开在遍历中使用count查询的目的。
public function tagslst($num) { debug('begin'); $tagRes=Db::name('tags')->select(); foreach ($tagRes as $key => $value) { $tagsnum=Db::name('art_tag')->where('tags_id',$value['id'])->count(); $tagsRes[$key]=array('sort'=>$tagsnum,'id'=>$value['id'],'tag_name'=>$value['tag_name']);//构造键名为sort,键值为count计数的新数组 } foreach ($tagsRes as $k => $v) { $sort[]=$v['sort']; } array_multisort($sort, SORT_DESC, $tagsRes);//按tags数多少重新排序数组 $tagsRes=array_slice($tagsRes,0,$num);//返回指定部分数据 debug('end'); dump(debug('begin','end').'s'); dump(debug('begin','end','m').'kb');die; return $tagsRes; }
测试结果:运行耗时0.05s,内存占用112k。如图:
上一篇是正常思维,通过查询tag表中的id在关联表中做count查询查询,最后以count依据截取需要的部分内容返回给控制器。缺陷在上一篇中提到,将第一步结果遍历后,代入count计数,有多少条数据就要查询多少次数据库,这个性能损失非常大。
今天换个思路来实现相同的目的。首先通过查询中间表中的tags_id列,将查询结果通过array_count_values
函数做一个计数操作(关键就在这里,通过使用数组来计数达到避开循环中使用count查询)。后续对这个数组截取需要的部分在tag表中使用in查询,返回最终查询结果即可。代码如下:
public function tagslst($num) { debug('begin'); $tagidRes=Db::name('art_tag')->field('tags_id')->select(); foreach ($tagidRes as $key => $value) { $tagids[]=$value['tags_id']; } $tagids=array_count_values($tagids); arsort($tagids); $tagids=array_slice($tagids,0,$num,true); foreach ($tagids as $k => $v) { $tag_idRes[]=$k; } $map['id']=['in',$tag_idRes]; $tagRes=Db::name('tags')->where($map)->select(); foreach ($tagRes as $key => $value) { foreach ($tag_idRes as $k => $v) { if($value['id']==$v){ $tagsRes[$k]=$value; } } } ksort($tagsRes); debug('end'); dump(debug('begin','end').'s'); dump(debug('begin','end','m').'kb');die; return $tagsRes; }
同样,也使用debug函数测试下相应的性能数据。得到结果如下:
和前面的数据进行对比可见,耗时节约70%,内存消耗减少50%以上。性能提升还是非常明显的。性能提升的关键在用PHP数组内置函数去代替了count计数查询,第二是截取需要的部分进行最后的数据查询。