數據庫性能優化2


概述

  TSQL語法習慣和規范

1,TSQL語法習慣和規范(一切不是教條主義)

  目標:編寫健壯的sql語句,生成更加高效的執行計划

    所有的性能優化中,理論基礎固然重要,但往往經驗比理論更重要;經驗說明你踩過的坑多;但解決問題的能力也建立在你的知識積累和思考

    你可以嘗試建立一些爛表,爛數據結構,然后嘗試優化它

    優秀的數據結構往往反映了你的領域模型

查詢語句

  下面我們以以下這條查詢語句來分析Sql的語法規范:

    UserInfo表,10萬行數據,主鍵Id,非聚集索引UserCode

    Employee表,100萬行數據,無任何索引

    Employee表中有一個UserId字段,用於記錄Employee對應的User   

select  * from   UserInfo  as a
join Employee as b on a.Id=b.UserId
where a.UserName='cmliu'

  1,需要明確需要返回的字段;盡可能避免"select *"語句

      減少IO數據量

      提高索引的覆蓋,提高索引的使用率

  2,需要限定返回集合數據量;尤其是數據量比較大的時候

      防止大批量的數據操作

      有效使用索引

      防止掃描操作帶來大量的磁盤IO和內存開銷

      考慮一下,那些需要返回全部數據的業務場景是否是合理的,是否可以用其他方案替代;

        大數據量時全部數據返回來,用戶能看得過來嗎?是否可以折中或者替代

  3,優先考慮使用索引;在需要對數據進行過濾的時候,優先考慮使用索引字段

      如果存在多個索引字段,那么我們優先考慮選擇重復率最低的索引字段

      一般情況下,我們會選擇重復率不超過5%的字段作為索引字段

  4,過濾字段上不要使用任何計算,包括函數邏輯計算

      計算會照成查詢優化器無法使用計算字段的索引

  按上面規范優化之后

select  top 10 a.UserName,a.UserCode,b.EmployeeName from   UserInfo  as a
join Employee as b on a.Id=b.UserId
where a.UserCode='cmliu'

  

  4,Order By:order by 子句的性能取決於參與排序的數據量的大小

    控制排序數據集的大小,排序是在數據篩選的結果完成后進行排序的,避免大數據量的排序操作

    排序消耗的資源超過內存限制時,排序過程中則會使用到TempDB,此時性能會大大下降

      因為TempDB是公共的,大批量數據排序甚至會導致整個系統出現大量的sql性能下降

    使用索引,尤其是必須針對大批量數據排序操作時

      排序合理使用索引甚至可以在查詢過程中不發生排序

  5,數據量級

    大批量的數據操作會導致將查詢中的大量數據從內存拆分到TempDB,TempDB是公共的,是存儲在磁盤上的,這會增加IO消耗

    大批量的數據操作會清理緩存,會使緩存失效

    數據量級建立在數據庫服務器硬件資源,網絡資源的性能與數據結構設計上

      對於有些系統來說100萬行就是大數據量,而針對有些系統來說1000萬行都是小數據量

    行業里面一般情況下將千萬級,億級數據量定為大數據量;常見的大數據量主要集中在流水,記錄等這些業務方面;如支付流水,訂單流水,交易流水,存取款流水,倉庫流水,定位記錄等

  6,Group By

    group by對數據進行分組統計時,也要使用排序算法;所以對於order by的優化是對group by的優化是一樣的;

    所以group by過程中可能會發生Hash計算或者排序計算,如果你在group by的字段合理的索引,就可以避免哈希計算和排序;如下圖

    考慮限制參與group by的數據量;因為發生Hash計算時,大數據量會更加消耗資源

    在全字段Group By時,你會發現group by與distinct是一致的;因為本質上distinct在計算時,就是進行一次全字段的group by;對比以下兩個sql語句的執行結果與執行計划,你就會明白

    注,下面的UserId,Age是有索引的,所以在group by時沒有發生排序

select distinct UserId,Age from SortUsers
select  UserId,Age from SortUsers group by UserId,Age

  

 

  Update語句

  Update語句執行時也會查詢目標數據;和Select相比;它們在鎖方面有差異

    Update會對數據優先添加【更新鎖】,確認要進行修改時,【更新鎖】轉換成【排他鎖】;然后才會更新數據

    Select使用的共享鎖,Update的排它鎖,更新鎖比共享鎖的兼容性更低;

    Update在更新大數據量的時候,或者Update存在性能問題時,或者Update長時間執行的,或者在一個事務中時,容易照成阻塞。

  Update的優化

    優先照顧Update語句;在更新頻繁,或者大數據量的更新時;優先考慮Update的性能,避免長時間阻塞,如update的索引,使用唯一字段來進行篩選過濾的數據

 

  Delete語句

    delete語句檢索數據的性能和Select是一樣的

    delete刪除數據時,使用【排他鎖】

    delete刪除數據時,會影響到索引的維護,對性能的要求更高;

    delete刪除語句的查詢字段使用索引時,應該權衡更新,查詢,刪除操作的頻率;不要因為過多的索引影響數據的刪除,更新的性能

    delete刪除數據時,為了保證ACID,會對刪除的數據記錄日志;大批量的數據刪除會造成大量的日志記錄,會影響性能

 

  Where子句

  sql的優化通常都是針對具有條件過濾(where)的語句進行的;沒有過濾條件的查詢語句只能選擇表掃描或者索引掃描

  where語句優化

    是否有合適的索引可供使用

    字段是否有函數計算

    返回字段集合(是否按需返回,返回的字段是否有索引)

    返回數據量

 

  關聯查詢

    嵌套循環是查詢連接中最好的一種方式,以小數據集作為外部數據,大數據集作為內部循環的集合

    連接查詢的連接字段優先使用索引字段,重復率低的字段

    嵌套連接以小表掃描(優先考慮索引掃描),大表查找為佳(優先考慮索引查找)

    數據集相當且已排序時,使用合並連接

    索引是寶貴的,也是昂貴,出現性能問題時,不是立馬對參與關聯查詢的所有表,所有參與查找或者連接字段健索引;而是找一個表,給1到2個字段建立索引;

    在大部分情況下(不要盲目),對大表建立索引的性價比會比較高。

    哈希連接算法偽代碼表示(實際上就是笛卡爾積):

foreach(var R1 in 小表){
    H1=Hash(R1.Key);
    Insert H1 into  HashBucket;   
}

foreach(var R2 in 大表){
    H2=Hash(R2.Key);
    foreach(var H1 in  HashBucket){
     if(H1=H2){
       輸出(R1,R2);
     } 
   }
}    

  

  子查詢

    子查詢盡量集中在where子句中,方便閱讀

    在一個與劇中,子查詢數量不超過3個,整個查詢語句涉及的表不超過5個

  子查詢的語句會被執行計划分解,簡化,特殊的轉換,轉成常用的連接操作

  在特殊的情況下,子查詢不能被優化或者簡化,在這些情況下,子查詢會優先執行,作為下一個操作的輸入部分

  過於復雜的子查詢會造成性能上的瓶頸  

    避免在子查詢中對大數據集進行匯總或者排序操作

    盡量縮小子查詢中可能返回的結果集范圍

    優先考慮使用確定性的判斷符(等於,in,exsit),避免使用any,all

    exist在子查詢中通常會轉換成inner join

    in在子查詢中通常會直接轉換成連接運算符

    如下示例圖

    

 

  性能優化工具

    sqlserver2017具有自動優化功能

    sqlserver2017智能查詢處理:自使用查詢處理

    性能監控和優化

    查詢存儲

      查詢存儲是數據庫性能優化的基礎,當sql性能出現問題,而我們是無法獲取這個sql的執行計划;

      而查詢存儲就是收集當時的執行信息存儲在磁盤中,

        包括執行計划,運行時統計信息,等待信息;

        你可以在查詢存儲中看到耗時的查詢,回歸的查詢等

      qlserver2017開啟查詢存儲會對數據庫造成3%-5%的性能影響;默認情況下是不開啟的

      sqlserver2017開啟查詢存儲方法一:使用sql

SET QUERY_STORE = ON (OPERATION_MODE = READ_WRITE);

      sqlserver2017開啟查詢存儲方法二:在sql server mangement studio中,選擇要監控的數據庫,右鍵"屬性",在屬性面版中,選擇查詢存儲>操作模式,修改值為"讀寫"

        在數據查詢存儲的配置面板上有一個數據刷新間隔;默認15分鍾,數據刷新間隔小會影響到數據庫性能

      

      查詢存儲的結果

      

      執行計划回歸

        執行計划可能會因為內存的壓力清除,也可能會因為數據的趨勢,索引而變更;

        執行計划的變更會可能導致相同的sql語句采用不同的執行計划;一般情況下,新的執行計划會比舊的執行計划要好

        也存在新的執行計划沒有舊的執行計划好的情況;這樣新的執行計划就會導致性能回歸;

        在沒有查詢存儲的情況下,我們是無法發現執行計划回歸的;查詢回歸,

    參數嗅探

      sqlserver編譯sql時會評估傳入的參數,生成對應的執行計划緩存,參數值會保存在執行計划緩存中

    自動優化

      對潛在查詢性能問題進行深入分析,並提供優化建議;自動選擇更好的執行計划;當數據庫引擎發現更好的執行計划時,會自動更正執行計划

      sql server要執行多次來搜集執行計划的信息

      影響執行計划質量的因素:統計信息果實,不合理的索引,低效的sql語句,代碼重編譯

      自學習,持續監控

      開啟自動調優sql:

alter database current
set AUTO_TUNING(FORCE_LAST_GOOD_PLAN=ON);

  

 


免責聲明!

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



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