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