轻易不要用多表查询,如下代码以及模拟数据测试结果能让你清晰地认识到多表查询,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;
}