1. 程式人生 > >數據庫優化的三個例子

數據庫優化的三個例子

mysql

在維護舊數據庫的時候經常碰到非常的查詢,多數都是兩方面的原因。
1)沒有加索引
2)查詢語句導致索引用不上
3)過多的連接數據庫


例子1:

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

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 這條語句,仔細分析數據庫才知道並沒有相關的索引作用在這條查詢語句上,這樣導致了這條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 連接的查詢條件拆出來,分別進行查詢,最後用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 類型,再增加一個索引到這個字段。這樣這個查詢慢的問題就徹底得到解決了。

例子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次。所以單獨查一條沒有性能問題,但是如果多次重復這樣的查詢就會引起性能問題。

解決的方法就是在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的查詢結果。所以我們先用一次查詢把數據庫結果組成了數組。

數據庫優化的三個例子