資料庫效能優化2

席小發表於2020-09-12

概述

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

  

 

相關文章