关于ThinkPHP中网站性能优化研究

轻易不要用多表查询,如下代码以及模拟数据测试结果能让你清晰地认识到多表查询,join,left join,inner join的可怕之处,能不用就不要用

/**
     * 获取持有偶像币总估值
     * @param $uid
     * @return mixed
     * @author zzl [email protected]
     * 时间:2019.06.18
     */
    public function getHasCoinExpect($uid){
        $tag='_User_total_tnk_'.$uid;
        $total=S($tag);
        if($total===false){
            $total=0;
            //获取持有偶像币估值
            $page=1;//分页查询,防止某个用户拥有太多种偶像币,如管理员
            $row=5000;
            do{
                //测试idol_coin_has表40W条记录,用户10W条记录,单次循环耗时0.1s左右
                $idol_has_list=$idol_uids=$idol_price_list=array();
                $price=$has_num=0;
                $idol_has_list=$this->where(array('uid'=>$uid,'has_num'=>array('gt',0)))->page($page,$row)->order('has_num desc,idol_uid asc')->getField('idol_uid,has_num');
                $idol_uids=array_keys($idol_has_list);
                $idol_price_list=D('Idol/Idol')->where(array('uid'=>array('in',$idol_uids),'single_price'=>array('gt',0),'status'=>1))->getField('id,uid,single_price');//这里增加single_price字段,初始化时为0,有交易时改变
                foreach ($idol_price_list as $vo){
                    $price=$vo['single_price'];
                    $has_num=$idol_has_list[$vo['uid']];
                    $total=bc_add(bc_mul($has_num,$price,4),$total,4);
                }
                unset($vo);
                $page++;
            }while(count($idol_has_list)==$row);
            S($tag,$total,60);
            //测试idol_coin_has表40W条记录,要查询用户的偶像币持有条数有10W条记录,总耗时1.5~2.5s之间
        }
        return $total;

        //如下注释方案,采用inner join方式 测试idol_coin_has表40W条记录,用户10W条记录,直接崩溃,无响应,所以舍弃该方案
        /*if(1||$total===false){
            $total=0;
            //获取持有偶像币估值
            $page=1;//分页查询,防止某个用户拥有太多种偶像币,如管理员
            $row=500;
            G('d');
            $sql="select
    table1.uid,table1.idol_uid,table1.has_num,table2.single_price
    from
    __PREFIX__idol_coin_has as table1
    inner join __PREFIX__idol_info as table2
    on
    table1.uid={$uid} AND table1.has_num>0 AND table2.single_price>0 AND table1.idol_uid = table2.uid
    ORDER BY table1.has_num desc,table2.single_price desc ";
            do{
               G('a');
                $sql_do=$sql."limit ".($page-1)*$row.",".$row;
                $list=$this->query($sql_do);
                foreach ($list as $val){
                    $total=bc_add(bc_mul($val['has_num'],$val['single_price'],4),$total,4);
                }
                unset($val);
                dump($total);
                dump($page);
                $page++;
                G('b');
                dump(G('a','b'));
            }while(count($list)==$row);
            dump('e');
            dump(G('d','e'));exit;
                S($tag,$total,60);
        }*/
    }

 

对于大批量的操作,千万不要在foreach中出现sql查询,如下代码:

一条一条save的方案,每条操作都要0.01s左右,10W条的话,需要100s,是当前方案耗时的10倍,所以这里采用删除老记录,批量新增新记录的方式操作
/**
     * 重新计算排名
     * @return boolean
     * @author 郑钟良([email protected])
     * @date slf
     */
    private function _calculateRanking()
    {
        $heatModel=D('idol_heat');
        $map['status']=1;
        $order='heat desc,uid asc';
        $page=1;
        $row=5000;
        $rank=1;
        $this->startTrans();
        do{
            //用9W条数据测试,如果每条都需要改,单次循环需要0.55s,全部执行完需要11s左右
            //用9W条数据测试,如果有1W条数据需要改,单次循环需要0.15s,全部执行完需要3s左右
            //实际使用环境中,每次改变不会超过十分之一,而且总用户量很难达到10W,所以,该方案可行
            $heatList=$heatModel->where($map)->order($order)->page($page,$row)->select();
            $rank_list=$del_ids=array();
            foreach ($heatList as $val){
                if($val['last_rank']!=$val['rank']||$val['change_rank']!=$rank-$val['rank']||$val['rank']!=$rank){
                    $val['last_rank']=$val['rank'];
                    $val['change_rank']=$rank-$val['rank'];
                    $val['rank']=$rank;
                    $rank_list[]=$val;
                    $del_ids[]=$val['uid'];
                }
                $rank++;
            }
            unset($val);
            if(count($del_ids)){
                //一条一条save的方案,每条操作都要0.01s左右,10W条的话,需要100s,是当前方案耗时的10倍,所以这里采用删除老记录,批量新增新记录的方式操作
                $res=$heatModel->where(array('uid'=>array('in',$del_ids)))->delete();
                $res1=$heatModel->addAll($rank_list);
            }else{
                $res=1;
                $res1=1;
            }
            if(!$res||!$res1){
                break;
            }
            $page++;
        }while(count($heatList)==$row);
        if($res&&$res1){
            if($this->commit()){
                return true;
            }
        }else{
            $this->rollback();
        }
        return false;
    }

 

转载于:https://my.oschina.net/zzlzheng/blog/3077278

你可能感兴趣的:(关于ThinkPHP中网站性能优化研究)