數據庫優化打怪升級


  最近呢,總是有人反饋公司的一個系統頁面反應太慢,一個列表展示,竟然需要二三十秒,我上去點了點,發現沒二三十秒那么誇張,但是也是夠慢了,龜速,直接影響工作效率啊。所以從這星期開始,我便硬着頭皮上優化系統了。

  這個系統使用MVC3+Easy UI做的,數據庫用的是SQL Server 2012。優化呢,首先就是定位原因,剛開始呢,只能按傳統方法來,想想瓶頸在哪,網絡傳輸,程序響應和頁面渲染,磁盤IO。前兩個我是沒法控制了,只有想想最后的法子了,從數據庫入手了。

  

  上圖就是執行頁面很緩慢的界面。首先監控到其sql語句,因為手里有源代碼,直接就用程序調試獲取了,不用 SQL Server Profiler。拿到sql語句后,就開始對sql進行分析,看看哪里能進行優化。一條真理:盡可能的避免全表掃描。

  

SELECT TOP 10 *    FROM 
(     
   SELECT ROW_NUMBER() OVER (ORDER BY CREATEDATE DESC) AS RowNumber,b.* FROM 
   ( 
       SELECT     WO.WorkOrderCode,WO.WorkOrderType,WO.Status,WO.CustName,WO.CustTel1,WO.CustTel2,WO.CustIdentityNo,WO.CardId,WO.CustSex,WO.CustAge,WO.CustBirthday, WO.IsMember,WO.AccountLevel,WO.MemberDate,WO.AccountScore,WO.DestTarget,WO.DestTargetDesc,WO.Source,WO.ToType,WO.Dept,WO.HotelId,WO.HotelName, WO.HotelCreateDate,WO.HotelType,WO.HotelBrand,WO.HotelTel,WO.HotelBigArea,WO.HotelArea,WO.HotelCityGroup,'' AS HotelCity,WO.Title,WO.SelectTitle,WO.Content,WO.IsAccept,WO.CompensateDept, WO.CompensateAmountType,WO.CompensateAmount,WO.ReferDept,WO.ResponsiblePerson,W2.Attribute3 AS ResponsiblePersonName,WO.ResponsibleDate,WO.ClosePerson, WO.CloseDate,WO.CatePerson,WO.CateDate,WO.ScorePerson,WO.ScoreDate,WO.CreateUser,WO.CreateDate,WO.UpdateUser,WO.UpdateDate,W1.Attribute3 AS UserName   FROM WorkOrder WO WITH (NOLOCK)  LEFT JOIN WSCUser W1 ON WO.CreateUser=W1.UserID  LEFT JOIN WSCUser W2 ON WO.ResponsiblePerson=W2.UserID 
       WHERE 1=1  AND (WO.Title LIKE '%其他%' OR WO.SelectTitle LIKE '%其他%')  AND WO.CreateDate BETWEEN '2017-09-03 00:00:00' AND '2017-09-30 23:59:59' 
     AND WO.STATUS=3 AND W2.Attribute3 ='張三'  AND WO.WorkOrderType='T' AND WO.IsDelete=0                                                                                       
    ) b   
) a  WHERE RowNumber > 0 order by rownumber

 

 1. 從里往外看吧,最里層的子查詢,是3個表的左連接查詢,這個地方可以采用的方案有兩種:

        ①在連接字段WO表的字段CreateUser上建立索引,②把這個聯合查詢做成視圖(此法效果不好)。

   2.之后再看where后邊,

        ①Title中用了like查詢,可以改成=,但是標題那個地方要求有模糊搜索功能(此法不可取)。

        ②對於status(三種狀態),workordertype(四種狀態),IsDelete(兩種狀態)這些建立索引,但是索引建立之后,效果很差,因為對於這種區別度很小的字 段,建立索引后,查詢時還會增加 KeyLookup(標簽查找 ),性能並不一定會提升(此法效果不好)。

        ③對於這種條件不固定的查詢,基本上可以把多列索引這種方案給排除了,查詢出來二三十個字段,也可以把覆蓋索引給排除了(此法不可取)。

        ④最后只能選擇在CreateDate字段上建立索引了(此法可取)。

       3.對於分頁算法,分頁算法有好幾種,經過考慮,這種用ROW_NUMBER()的方法效率還可以。所以說換一個分頁算法,需要改大量程序代碼,這種方法也是不可取的。

    綜上所述,選擇的方法只有是在CreateDate上建立索引了。

    4.之后還有一部操作,如下圖所失,將索引重新組織一下,因為時間長了,碎片比較多。

   

注意:進行重新組織索引時,一定不要在程序訪問量大時進行,因為會造成鎖表,系統直接無法使用。這個過程是很有風險的,數據量太大,執行也是需要一定時間的。

    本來也為好了,可是故事未完待續,我把這段sql在數據庫中執行,發現用時只有0.3s左右,但是系統中能看到結果,還需要1.4s左右。我剛開始還以為是系統代碼太爛,EasyUI框架太重的原因。又調試了一會兒才發現是,程序執行了分頁語句之后,又執行了一段算數據總數的sql,原來是count()惹的貨。

 

 

 

 

select count(*) from 
(
      SELECT   WO.WorkOrderCode,WO.WorkOrderType,WO.Status,WO.CustName,WO.CustTel1,WO.CustTel2,WO.CustIdentityNo,WO.CardId,WO.CustSex,WO.CustAge,WO.CustBirthday, WO.IsMember,WO.AccountLevel,WO.MemberDate,WO.AccountScore,WO.DestTarget,WO.DestTargetDesc,WO.Source,WO.ToType,WO.Dept,WO.HotelId,WO.HotelName, WO.HotelCreateDate,WO.HotelType,WO.HotelBrand,WO.HotelTel,WO.HotelBigArea,WO.HotelArea,WO.HotelCityGroup,'' AS HotelCity,WO.Title,WO.SelectTitle,WO.Content,WO.IsAccept,WO.CompensateDept, WO.CompensateAmountType,WO.CompensateAmount,WO.ReferDept,WO.ResponsiblePerson,W2.Attribute3 AS ResponsiblePersonName,WO.ResponsibleDate,WO.ClosePerson, WO.CloseDate,WO.CatePerson,WO.CateDate,WO.ScorePerson,WO.ScoreDate,WO.CreateUser,WO.CreateDate,WO.UpdateUser,WO.UpdateDate,W1.Attribute3 AS UserName   FROM WorkOrder WO WITH (NOLOCK)  LEFT JOIN WSCUser W1 ON WO.CreateUser=W1.UserID  LEFT JOIN WSCUser W2 ON WO.ResponsiblePerson=W2.UserID 
       WHERE 1=1  AND (WO.Title LIKE '%其他%' OR WO.SelectTitle LIKE '%其他%')  AND WO.CreateDate BETWEEN '2016-05-03 00:00:00' AND '2017-09-30 23:59:59' 
     AND WO.STATUS=3    AND WO.WorkOrderType='T' AND WO.IsDelete=0     
) a

 

 

上圖就是算總數據量的語句,這個語句的優化可以這樣。

  1.count(*)可以改成count(WorkOrderCode),WorkOrderCode是WorkOrder的主鍵,經過測試執行效率要快於count(*)。--詳情請見

  2.將查詢字段全部去掉。

  優化完成之后,語句變成這樣。

  

SELECT count(WorkOrderCode) FROM WorkOrder WO WITH (NOLOCK)  LEFT JOIN WSCUser W1 ON WO.CreateUser=W1.UserID  LEFT JOIN WSCUser W2 ON WO.ResponsiblePerson=W2.UserID 
       WHERE 1=1  AND (WO.Title LIKE '%其他%' OR WO.SelectTitle LIKE '%其他%')  AND WO.CreateDate BETWEEN '2016-05-03 00:00:00' AND '2017-09-30 23:59:59' 
     AND WO.STATUS=3    AND WO.WorkOrderType='T' AND WO.IsDelete=0     

 

    到這里,優化算是正式結束了,搞了三四天。在這過程中也對數據庫的索引,語句優化有了更加深刻的認識。當然有些也顛覆了我以前看到理論,如聯合查詢建成視圖后,查視圖效率會提升,但是我做的實驗並非如此,視圖效率不如聯合索引(詳情實驗--視圖查詢性能的新認識)。

  拋開技術上的因素,還有很多業務上的需要考慮,像剛開始的那個查詢條件表單,有些選項可能就很少使用,但剛開始我也考慮了很多種情況,后來,跟客服人員溝通,發現他們有些選項就不怎么用,之后我按照他們給的經常使用的篩選條件,進行優化,也就不那么盲目了,問題也好處理了。通過這件事呢,總結出兩條哲理:“干活兒不由東,累死也誤工”、“溝通很重要”。

     世事洞明皆學問 人情練達即文章,技術始終是要為人而服務的,所以不要為了技術而技術。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM