在維護(hù)舊數(shù)據(jù)庫的時候經(jīng)常碰到非常的查詢,多數(shù)都是兩方面的原因。
1)沒有加索引
2)查詢語句導(dǎo)致索引用不上
3)過多的連接數(shù)據(jù)庫
成都創(chuàng)新互聯(lián)公司2013年開創(chuàng)至今,先為靈璧等服務(wù)建站,靈璧等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為靈璧企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
在一個大型的計(jì)算中原來每天要花費(fèi)半小時才能完成,對計(jì)算的過程進(jìn)行仔細(xì)的分析,發(fā)現(xiàn)下面的語句花費(fèi)了很長時間
select sum(order_qty - delivery_qty - reduce_confirm_qty - lost_qty ) qty from circle_ordering where sku = '" . $sku . "' AND submit_status = 5 AND order_type = 'AIR'
通過explain 這條語句,仔細(xì)分析數(shù)據(jù)庫才知道并沒有相關(guān)的索引作用在這條查詢語句上,這樣導(dǎo)致了這條sql是全表查詢。于是對這三列(sku, submit_status, order_type)新建索引. 重新執(zhí)行后,整個程序只用了10份鐘就完成了。
select a.ebay_id, b.ebay_id as ebay_subid, from_unixtime(a.ebay_paidtime) as ebay_paidtime,
a.ebay_account, a.ebay_countryname, c.store_name as warehouse, a.ebay_carrier,
b.sku, b.ebay_amount, a.ebay_currency, b.ebay_itemprice,
b.shipingfee, ((b.ebay_itemprice*b.ebay_amount)+b.shipingfee) as total_amount, ebay_postcode,
b.item_promotion_discount_amount, b.ship_promotion_discount_amount
from ebay_order a left join ebay_orderdetail b on(a.ebay_ordersn=b.ebay_ordersn)
left join ebay_store c on (a.ebay_warehouse = c.id)
where a.ebay_combine !=1 and (a.resend_org_ebay_id=0 or a.resend_org_ebay_id is null) and
b.ebay_amount >0 and a.ebay_warehouse !='' and a.ebay_user='manwei'
and
(
a.ebay_paidtime between UNIX_TIMESTAMP('".$astart."') and UNIX_TIMESTAMP('".$aend."')
or
(a.ebay_paidtime not between UNIX_TIMESTAMP('".$astart_p."') and UNIX_TIMESTAMP('".$aend_p."') and
a.shippedtime between UNIX_TIMESTAMP('".$astart_p."') and UNIX_TIMESTAMP('".$aend_p."')) ";
if($last_ebay_id!='') $data .= " or a.ebay_id >='".$last_ebay_id."'";
$data .= ") order by a.ebay_id, b.ebay_id ";
注意這個復(fù)雜的查詢語句的條件
第一個條件
(a.ebay_paidtime between UNIX_TIMESTAMP('".$astart."') and UNIX_TIMESTAMP('".$aend."')
由于在ebay_paidtime字段有索引,如果只有這個條件,查詢速度很快,查詢一次不到一秒。但是因?yàn)楹竺孢€有兩個條件使用了 or, 這樣導(dǎo)致會導(dǎo)致了對ebay_order進(jìn)行了全表查詢,而這個表有3百多萬條數(shù)據(jù),所以查詢非常慢。
(有這個說法 :驗(yàn)證在兩個相同字段之間使用or不會導(dǎo)致全表掃描,只有出現(xiàn)不同字段自建使用or時會導(dǎo)致全表掃描。但我沒有驗(yàn)證過。)
根據(jù)業(yè)務(wù)需求我們把三個用or 連接的查詢條件拆出來,分別進(jìn)行查詢,最后用union語句連起來。這樣查詢的效率得到了大大的提高。修改后的查詢?nèi)缦?/p>
$data1 ="select " . $fields_list . "
from ebay_order a left join ebay_orderdetail b on(a.ebay_ordersn=b.ebay_ordersn)
left join ebay_store c on (a.ebay_warehouse = c.id)
where a.ebay_combine !=1 and (a.resend_org_ebay_id=0 or a.resend_org_ebay_id is null) and
b.ebay_amount >0 and a.ebay_warehouse !='' and a.ebay_user='manwei'
and a.ebay_paidtime between UNIX_TIMESTAMP('".$astart."') and UNIX_TIMESTAMP('".$aend."')";
$data2 = "select " . $fields_list . "
from ebay_order a left join ebay_orderdetail b on(a.ebay_ordersn=b.ebay_ordersn)
left join ebay_store c on (a.ebay_warehouse = c.id)
where a.ebay_combine !=1 and (a.resend_org_ebay_id=0 or a.resend_org_ebay_id is null) and
b.ebay_amount >0 and a.ebay_warehouse !='' and a.ebay_user='manwei'
and (
a.shippedtime between UNIX_TIMESTAMP('".$astart_p."') and UNIX_TIMESTAMP('".$aend_p."') and
a.ebay_paidtime not between UNIX_TIMESTAMP('".$astart."') and UNIX_TIMESTAMP('".$aend."')
)";
if($last_ebay_id!='') {
$data3 = "select " . $fields_list . "
from ebay_order a left join ebay_orderdetail b on(a.ebay_ordersn=b.ebay_ordersn)
left join ebay_store c on (a.ebay_warehouse = c.id)
where a.ebay_combine !=1 and (a.resend_org_ebay_id=0 or a.resend_org_ebay_id is null) and
b.ebay_amount >0 and a.ebay_warehouse !='' and a.ebay_user='manwei'
and a.ebay_id >='" .$last_ebay_id ."'";
}
$data = "(" . $data1 . ")";
if($data2 != "") $data = $data . " union (". $data2 . ")";
if($data3 != "") $data = $data . " union (". $data3 . ")";
小插曲,當(dāng)我們分析data2的時候,無論如何給shippedtime加索引,只要查詢shippedtime都是全表查詢。仔細(xì)分析才知道原來在數(shù)據(jù)庫設(shè)計(jì)的時候,這個shippedtime的字段是varchar, 程序把時間戳保存成這種類型,自然沒有辦法使用適合我們需要的索引,解決的方法是通過alter語句先把shippedtime改成int 類型,再增加一個索引到這個字段。這樣這個查詢慢的問題就徹底得到解決了。
$data = $isfesdb->query($data);
$quan = $isfesdb->num_rows($data);
for($i=0;$i<$quan;$i++){
{
...
$vv = "select goods_name, goods_weight from ebay_goods where goods_sn='".$sku[$i]."' limit 1";
$vv = $isfesdb->execute($vv);
$vv = $isfesdb->getResultArray($vv);
if(count($vv)==0){
...
$sku[$i] = str_replace('-FBA-FR','',$sku[$i]);
...
}
...
}
從代碼上看,這個只是很簡單的查詢,ebay_goods也有索引,應(yīng)該很快就能查詢到結(jié)果。但實(shí)際上整個流程跑下來很慢。仔細(xì)分析原因是因?yàn)?quan的數(shù)字太大,導(dǎo)致了for循環(huán)超過了10000次,這樣導(dǎo)致了$vv這個查詢進(jìn)行了10000次。所以單獨(dú)查一條沒有性能問題,但是如果多次重復(fù)這樣的查詢就會引起性能問題。
解決的方法就是在for循環(huán)的前面先查詢ebay_goods全表,把這個表記錄到一個數(shù)組,然后在for循環(huán)里使用素組的數(shù)據(jù)。因?yàn)閑bay_goods這個數(shù)組只有幾千條記錄,這個方法是可行的。
修改程序變成:
$vv = $isfesdb->query("select goods_sn, goods_name, goods_weight from ebay_goods");
$vv_quan = $isfesdb->num_rows($vv);
$vv_result = $isfesdb->getResultArray($vv);
for($i=0; $i<$vv_quan; $i++) {
$goods_array[$vv_result[$i]['goods_sn']] = array($vv_result[$i]['goods_name'], $vv_result[$i]['goods_weight']);
}
for($i=0;$i<$quan;$i++)
{
...
if(!array_key_exists($sku[$i], $goods_array)){
...
$sku[$i] = str_replace('-FBA-FR','',$sku[$i]);
...
}
...
}
我們采用數(shù)組的方法后,查詢也比舊方法效率提高好幾倍。這是因?yàn)楝F(xiàn)在我們的服務(wù)器配置的內(nèi)存是足夠大的,PHP的運(yùn)行也是足夠快的。瓶頸就在于php在等待MySQL的查詢結(jié)果。所以我們先用一次查詢把數(shù)據(jù)庫結(jié)果組成了數(shù)組。