數據庫優化的三個例子

在維護舊數據庫的時候常常碰到很是的查詢,多數都是兩方面的緣由。
1)沒有加索引
2)查詢語句致使索引用不上
3)過多的鏈接數據庫php


例子1:

在一個大型的計算中原來天天要花費半小時才能完成,對計算的過程進行仔細的分析,發現下面的語句花費了很長時間mysql

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'sql

經過explain 這條語句,仔細分析數據庫才知道並無相關的索引做用在這條查詢語句上,這樣致使了這條sql是全表查詢。因而對這三列(sku, submit_status, order_type)新建索引. 從新執行後,整個程序只用了10份鍾就完成了。數據庫

例子2:

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 ";

注意這個複雜的查詢語句的條件數組

第一個條件
(a.ebay_paidtime between UNIX_TIMESTAMP('".$astart."') and UNIX_TIMESTAMP('".$aend."')
因爲在ebay_paidtime字段有索引,若是隻有這個條件,查詢速度很快,查詢一次不到一秒。可是由於後面還有兩個條件使用了 or, 這樣致使會致使了對ebay_order進行了全表查詢,而這個表有3百多萬條數據,因此查詢很是慢。
(有這個說法 :驗證在兩個相同字段之間使用or不會致使全表掃描,只有出現不一樣字段自建使用or時會致使全表掃描。但我沒有驗證過。)服務器

根據業務需求咱們把三個用or 鏈接的查詢條件拆出來,分別進行查詢,最後用union語句連起來。這樣查詢的效率獲得了大大的提升。修改後的查詢以下數據庫設計

$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 . ")";

小插曲,當咱們分析data2的時候,不管如何給shippedtime加索引,只要查詢shippedtime都是全表查詢。仔細分析才知道原來在數據庫設計的時候,這個shippedtime的字段是varchar, 程序把時間戳保存成這種類型,天然沒有辦法使用適合咱們須要的索引,解決的方法是經過alter語句先把shippedtime改爲int 類型,再增長一個索引到這個字段。這樣這個查詢慢的問題就完全獲得解決了。ide

例子3:

$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也有索引,應該很快就能查詢到結果。但實際上整個流程跑下來很慢。仔細分析緣由是由於$quan的數字太大,致使了for循環超過了10000次,這樣致使了$vv這個查詢進行了10000次。因此單獨查一條沒有性能問題,可是若是屢次重複這樣的查詢就會引發性能問題。post

解決的方法就是在for循環的前面先查詢ebay_goods全表,把這個表記錄到一個數組,而後在for循環裏使用素組的數據。由於ebay_goods這個數組只有幾千條記錄,這個方法是可行的。
修改程序變成:性能

$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]);                                   
             ...

     }

 ...
 }

咱們採用數組的方法後,查詢也比舊方法效率提升好幾倍。這是由於如今咱們的服務器配置的內存是足夠大的,PHP的運行也是足夠快的。瓶頸就在於php在等待mysql的查詢結果。因此咱們先用一次查詢把數據庫結果組成了數組。

相關文章
相關標籤/搜索