Expert 診斷優化系列------------------語句調優

来源:http://www.cnblogs.com/double-K/archive/2016/05/31/5544439.html
-Advertisement-
Play Games

前面三篇通過CPU、記憶體、磁碟三巨頭,講述瞭如何透過現在看本質,怎樣定位伺服器三巨頭反映出的問題。為了方便閱讀給出鏈接: Expert 診斷優化系列 你的CPU高麽? Expert 診斷優化系列 記憶體不夠用麽? Expert 診斷優化系列 冤枉磁碟了 通過三篇文章的基本介紹,可以看出系統的語句如果不 ...


    前面三篇通過CPU、記憶體、磁碟三巨頭,講述瞭如何透過現在看本質,怎樣定位伺服器三巨頭反映出的問題。為了方便閱讀給出鏈接:

Expert 診斷優化系列------------------你的CPU高麽?

Expert 診斷優化系列------------------記憶體不夠用麽?

Expert 診斷優化系列------------------冤枉磁碟了

    通過三篇文章的基本介紹,可以看出系統的語句如果不優化,可能會導致三巨頭都出現異常的表現。所以本篇開始介紹系統中的重頭戲--------------SQL語句!

  • 開篇前的啰嗦 

    什麼是SQL 語句 ?

    

   這就是SQL 語句! 帥氣吧!還有呢!

  

   

 

   這也是SQL語句!

 

 

   博主真能騙人,我讀書少也知道,這是“車、馬、炮”的 “車” ! 沒錯,此篇文章里會以“車”來代表你的SQL 語句,讓你知道怎樣讓你的“車”從16手報廢車改裝成------------------ |法拉利|

   註:SQL語句優化的細節,一本書都寫不全,所以這裡只講述“改裝思想!”

  • 改裝有順序------常開的愛車下手

   你的系統中有成千上萬的語句,那麼優化語句從何入手呢 ? 當然是系統中運行最頻繁,最核心的語句了。廢話不多說,上例子:

   

   這是一天的語句執行情況,裡面柱狀圖表示的是對應執行時間段內語句的次數,總體看起來長時間語句非常多。

   下麵看一下具體的語句執行情況:

   

 

   排位第一的語句執行次數38508次,是一個存儲過程(RPC:Completed 表示存儲過程結束,不知道這個的請看profiler的使用說明)。其中的一條語句(SP:StmtCompleted)也就是排在第二位的語句,存儲過程的執行時間大部分消耗這條子語句上!

這個例子可以看出業務系統中使用最頻繁,且遠遠高於其他處理的語句就是這個執行3W8Q多次的語句。

   那麼看到這樣的數據,想做優化當然要從這條語句下手了!它就是你最常開的車了! 這個例子中只要解決了這條語句的性能問題,整個系統性能就可以有一個質的飛躍。

 

--------------------------------上面的情況,你很專一,只喜歡開一種車!----------------------------

   這個例子中,你喜歡開的車就比較多了,也就是說需要你關註,並且優化的語句較多。(很多語句執行次數都很頻繁,也就是系統中使用到的頻繁功能較多)

   

 

   

 

   系統優化需要循序漸進,從系統最頻繁的語句出發,逐個解決語句問題。

   有人看到這會說,博主你有工具,能收集、能統計,我啥也沒有咋整?不要急後文腳本都會奉上

 

  • 改裝前的知識儲備

  知識儲備很重要,語句的優化涉及的地方很多很多,要麼為什麼說可以寫本書呢?

  1. 你知道什麼是執行計劃麽?如何在語句執行的同時,看到執行計劃?
  2. 你知道索引有幾種?有什麼區別麽?
  3. 你知道有索引和沒索引,語句執行的區別麽?
  4. 你知道什麼是統計信息麽?
  5. 你知道什麼是臨時表,表變數,CTE?有什麼區別?
  6. 什麼是事務?什麼是隔離級別?
  7. 你知道什麼是邏輯讀,什麼是物理讀,什麼是預讀麽?怎麼查看你執行消耗的IO資源?
  8. 你知道什麼是等待?怎麼查看你運行的語句是否在等待?等待反應出的問題是什麼?
  9. 你瞭解SQL的鎖機制麽?
  10. 你瞭解TempDB麽?什麼樣的語句會使用TempDB?
  11. 編譯與重編譯?
  12. 查詢提示是乾什麼的?
  13. .....
  14. .....
  15. .....
  16. .....
  • 常見的改裝方式

------------------------------------新手區-----------高手勿進-------------------------------------  

  是不是就沒法短時間內,掌握大部分語句的優化技巧呢? 這是可以的,簡單介紹一下語句簡單粗暴的調優方式:

  

開啟執行計劃,讓執行計劃告訴你,語句慢的原因

  

 

透過計劃,一眼看出索引


   

 

   當語句執行後,執行計劃中會提示你這條運行的語句中是否缺少索引,右鍵綠色部分"缺少索引提示",點擊缺少索引詳細信息,生成對應的索引腳本,創在在資料庫中。

   在次執行語句驗證是否有效,如果還繼續提示索引缺失,繼續按照此方法創建索引。

   

 

 

   索引對於一個語句的影響很大,一個有效的索引可以縮短語句的執行時間,並且降低CPU、IO、記憶體等消耗。也就是說不但讓你的語句執行快,更降低了寶貴的系統資源消耗!

   執行計劃中除了可以看出缺失的索引,也可以看出語句的主要消耗在哪。知道了主要消耗,我們也就可以針對這個消耗進行優化。如例子中94%的開銷在表的掃描上。當看到這個開銷很大並且是一個掃描的時候,第一反應要看掃描的表,有沒有篩選條件“where”條件,或 “關聯條件join" 如果有條件,那就看為什麼沒有先用條件過濾數據!是不是沒有索引? 是不是創建的索引不能用(隱式轉換?列上有函數?等等,具體為什麼不能使用索引,請自行百度) 

 

   高能提示:不要小看索引,感覺這都是小兒科。在我親身經歷的眾多客戶之中,大面積缺少索引的系統可以占到三層以上。或是軟體開發完,對資料庫就沒有建立索引,或是隨著系統的日積月累,數據量、功能也隨之增加,系統得不到一個及時的跟蹤優化導致。

 

    

降低語句的複雜度

  講一個我自己的故事,我剛從業的時候對資料庫的優化瞭解不深,一度認為自己寫的SQL 好牛逼,因為現在給我,我真是看不懂。一個語句兩張A4紙都列印不下!各種子查詢,視圖嵌套,函數嵌套,UNION ALL等等等。

不能否認這種語句寫出來以後,有種小自豪感!因為別人根本看不懂,改也改不了!這種語句在對於SQL 的優化器來說就是災難,下麵簡單的說下優化器拿到一條語句怎麼樣作出執行計劃:

  首先傳入一個語句,如果有視圖,則會把你視圖內的代碼和外層代碼經過二次編譯變成一個大語句(多層視圖都會編譯成一個),然後從表連接開始,優化器會根據統計信息,和一些預查詢(如執行所需要的欄位類型長度,數據量等)針對你的條件選用一個表作為驅動表,然後繼續和其他的表關聯,並選用關聯方式(hash、merge、nested loop)等,每次關聯順序和方式的都基於SQL的預估,也就是關聯的越多,最後的預估可能越不准確,進而導致選用一個比較差的計劃。為什麼有好的不選卻選出一個差的呢?因為優化器不會把你所有執行的可能都驗證一次,然後選擇一個最好的。這裡選出來的“最優”的只是一個相對值。

  介紹的有點跑題了,下麵我們說一下降低語句複雜度的常用方式:最常用的就是臨時表,比如先把條件篩選性較強的幾張表關聯,然後把結果放入臨時表,在用臨時表和其他表關聯。可以理解成我有10張表關聯,我先拿5張表出來關聯,然後把結果放入臨時表,再跟另外5張表關聯。這樣這個查詢的複雜度由10張表的聯合變成 5+6,這樣降低了複雜語句複雜度。

  複雜視圖也是如此,在視圖和外層關聯前,放入臨時表,再跟外層關聯。

  子查詢也是如此,可以分離出來成為臨時表的子查詢,先分離出來。

  

  情況很多種,最終目的就是降低語句複雜性,讓語句分多個步驟執行,這樣也可以讓優化器每次選出一個比較穩定的計劃(一個語句執行有時快有時慢,也很可能是語句的複雜性導致的)。

  

  高能提示:部分系統核心處理的語句比較複雜,且已經很多年前留下的遺產了,經歷了一代又一代,我真心不敢碰。那麼恭喜你中獎了,好好分析下業務,通過臨時表拆分語句還是有可能的!

       臨時表和表變數,最大的區別是表變數作為中間過程表不能插入太多數據,如果數據插入的多嚴重影響性能。

 

 

降低並行度,使用並行提升性能

  這個小標題好像有些矛盾!解釋一下降低並行度是因為現在的伺服器配置CPU數都很大64或更多的隨處可見,系統選用並行計劃時,使用過多的CPU 反而會使性能下降具體請參見:Expert 診斷優化系列------------------你的CPU高麽?

  首先看一個等待: CXPACKET

  

   

   CXPACKET 是最常見的等待之一,等待 並行計劃 CPU的調度,或線程上的資源等待,請參見

sys.dm_os_waiting_tasks 引發的疑問(上)

sys.dm_os_waiting_tasks 引發的疑問(中)

sys.dm_os_waiting_tasks 引發的疑問(下)

   當你看見如圖的等待情況時,說明你系統中並行度需要調整了!請參見系列中的CPU篇,這裡不過多介紹。

 

   另一種情況,語句可以通過並行來提升執行時間,這裡也不過多介紹,請參見SQL提示介紹-強制並行

使用一切方法降低讀次數

  一個語句運行起來消耗的讀次數也少,說可以間接說明這個語句優化程度較高,讀取的頁數少也會降低記憶體和磁碟的壓力。

   優化時可以開set statistics io on 來觀察語句的IO消耗情況。降低IO的主要方式就是添加索引和降低語句複雜度。

   註:重點關註讀次數多的表!

   

   這裡就不細說了!

 

  • 不能忽視的硬體問題

  前三篇一直在強調語句很影響伺服器資源。但不能忽略的一點就是,語句的運行好壞也很依賴於資源,硬體資源就好比路面環境。語句這車再好,路沒有那麼寬,也不平坦,再好的車也跑不起來。

反過來就算硬體足夠好,路夠寬也夠好,沒有好車也是跑不起來的!

  

 --------------博客地址---------------------------------------------------------------------------------------

Expert 診斷優化系列 http://www.cnblogs.com/double-K/

-----------------------------------------------------------------------------------------------------

  總結:語句運行的效率是系統的關鍵,而運行最頻繁的語句就是關鍵中的關鍵。找出系統運行頻率高且效率較差的語句進行優化,是優化思路中的核心。

     80%的優化不需要你有高深的技術積累,程咬金的三板斧輪上去,也會掃倒一大片的。請參見 ”常見改裝“中的三種手段。

     剩下20%的優化就需要對知識的不斷積累,在實際場景中獲得更好的知識提升。

     

     硬體和語句相互依賴,都是最優的那自然是好,但是作為技術人員我們保證系統語句是最優的,也是一種責任的體現!

     

     本文只是非常簡單的介紹常規優化的思路和方法,不足之處請諒解。後續文章中也會針對等待、執行計劃、tempDB等繼續細說系統的優化。

 

   PS: 優化需要使用各種手段,反覆嘗試才能達到一個最好的效果,優化無止境。

 -------------------------乾貨到了---------------------------------------------------------------------------    

 沒有自己的SQL工具怎麼找出執行頻繁的語句呢?

  1. profiler 對系統進行監控(不會的小伙伴,快去百度吧)  
  2. DMV視圖 ,篩選條件請自行修改

 

with aa as (
SELECT  
--執行次數 
QS.execution_count, 
--查詢語句 
SUBSTRING(ST.text,(QS.statement_start_offset/2)+1, 
((CASE QS.statement_end_offset WHEN -1 THEN DATALENGTH(st.text) 
ELSE QS.statement_end_offset END - QS.statement_start_offset)/2) + 1 
) AS statement_text, 
--執行文本 
ST.text, 
--執行計劃 
qs.last_elapsed_time,
qs.min_elapsed_time,
qs.max_elapsed_time,
QS.total_worker_time, 
QS.last_worker_time, 
QS.max_worker_time, 
QS.min_worker_time 
FROM 
sys.dm_exec_query_stats QS 
--關鍵字 
CROSS APPLY 
sys.dm_exec_sql_text(QS.sql_handle) ST 
WHERE 
QS.last_execution_time > '2016-02-14 00:00:00' and  execution_count > 500

-- AND ST.text LIKE '%%' 
--ORDER BY 
--QS.execution_count DESC

)
select text,max(execution_count) execution_count --,last_elapsed_time,min_elapsed_time,max_elapsed_time 
from aa
where [text] not  like '%sp_MSupd_%' and  [text] not like '%sp_MSins_%' and  [text] not like '%sp_MSdel_%' 
group by text
order by 2  desc

 

 

 

 怎麼查看自己系統缺失的索引?適合大批量創建索引

這裡的DMV信息只是記錄自上次SQL Server啟動以後的信息項,也就是說每次重啟之後這部分信息就丟失了,所以對於生產系統,建議確保運行了一段周期之後再進行查看。

在我們重新創建聚集索引的時候,SQL Server會預設的重新生成全部非聚集索引,如果表數據量特別大,這個過程會很漫長,如果不指定ONLINE的話,這個過程會是鎖定索引B-Teee的,這就意味著是阻塞的,業務就要停下來等待完成操作。

------------------缺失索引-----------------------
SELECT migs.group_handle, mid.* 
FROM sys.dm_db_missing_index_group_stats AS migs 
INNER JOIN sys.dm_db_missing_index_groups AS mig 
ON (migs.group_handle = mig.index_group_handle) 
INNER JOIN sys.dm_db_missing_index_details AS mid 
ON (mig.index_handle = mid.index_handle) 
WHERE migs.group_handle = 2
----------------------------------無用索引----------------------
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 
SELECT 
DB_NAME() AS DatbaseName 
, SCHEMA_NAME(O.Schema_ID) AS SchemaName 
, OBJECT_NAME(I.object_id) AS TableName 
, I.name AS IndexName 
INTO #TempNeverUsedIndexes 
FROM sys.indexes I INNER JOIN sys.objects O ON I.object_id = O.object_id 
WHERE 1=2 
EXEC sp_MSForEachDB 'USE [?]; INSERT INTO #TempNeverUsedIndexes 
SELECT 
DB_NAME() AS DatbaseName 
, SCHEMA_NAME(O.Schema_ID) AS SchemaName 
, OBJECT_NAME(I.object_id) AS TableName 
, I.NAME AS IndexName 
FROM sys.indexes I INNER JOIN sys.objects O ON I.object_id = O.object_id 
LEFT OUTER JOIN sys.dm_db_index_usage_stats S ON S.object_id = I.object_id 
AND I.index_id = S.index_id 
AND DATABASE_ID = DB_ID() 
WHERE OBJECTPROPERTY(O.object_id,''IsMsShipped'') = 0 
AND I.name IS NOT NULL 
AND S.object_id IS NULL' 
SELECT * FROM #TempNeverUsedIndexes 
ORDER BY DatbaseName, SchemaName, TableName, IndexName 
DROP TABLE #TempNeverUsedIndexes

--------------------------經常被大量更新,但是卻基本不適用的索引項--------------------
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 
SELECT 
DB_NAME() AS DatabaseName 
, SCHEMA_NAME(o.Schema_ID) AS SchemaName 
, OBJECT_NAME(s.[object_id]) AS TableName 
, i.name AS IndexName 
, s.user_updates 
, s.system_seeks + s.system_scans + s.system_lookups 
AS [System usage] 
INTO #TempUnusedIndexes 
FROM sys.dm_db_index_usage_stats s 
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] 
AND s.index_id = i.index_id 
INNER JOIN sys.objects o ON i.object_id = O.object_id 
WHERE 1=2 
EXEC sp_MSForEachDB 'USE [?]; INSERT INTO #TempUnusedIndexes 
SELECT TOP 20 
DB_NAME() AS DatabaseName 
, SCHEMA_NAME(o.Schema_ID) AS SchemaName 
, OBJECT_NAME(s.[object_id]) AS TableName 
, i.name AS IndexName 
, s.user_updates 
, s.system_seeks + s.system_scans + s.system_lookups 
AS [System usage] 
FROM sys.dm_db_index_usage_stats s 
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] 
AND s.index_id = i.index_id 
INNER JOIN sys.objects o ON i.object_id = O.object_id 
WHERE s.database_id = DB_ID() 
AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0 
AND s.user_seeks = 0 
AND s.user_scans = 0 
AND s.user_lookups = 0 
AND i.name IS NOT NULL 
ORDER BY s.user_updates DESC' 
SELECT TOP 20 * FROM #TempUnusedIndexes ORDER BY [user_updates] DESC 
DROP TABLE #TempUnusedIndexes

 

 ----------------------------------------------------------------------------------------------------

註:此文章為原創,歡迎轉載,請在文章頁面明顯位置給出此文鏈接!
若您覺得這篇文章還不錯請點擊下右下角的推薦,非常感謝!

  引用高大俠的一句話 :“拒絕SQL Server背鍋,從我做起!”


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、基礎 1、說明:創建資料庫 2、說明:刪除資料庫 3、說明:備份sql server 4、說明:創建新表 5、說明:刪除新表 6、說明:增加一個列 7、說明:添加主鍵 8、說明:創建索引 9、說明:創建視圖 10、說明:幾個簡單的基本的sql語句 11、說明:幾個高級查詢運算詞 A: UNION ...
  • MySQL的高可用方案一般有如下幾種: keepalived+雙主,MHA,MMM,Heartbeat+DRBD,PXC,Galera Cluster 比較常用的是keepalived+雙主,MHA和PXC。 對於小公司,一般推薦使用keepalived+雙主,簡單。 下麵來部署一下 配置環境: 角 ...
  • Table A aid adate 1 a1 2 a2 3 a3 TableB bid bdate 1 b1 2 b2 4 b4 兩個表a,b相連接,要取出id相同的欄位 select * from a inner join b on a.aid = b.bid這是僅取出匹配的數據. 此時的取出的是 ...
  • 資料庫是要被廣大客戶所共用訪問的,那麼在資料庫操作過程中很可能出現以下幾種不確定情況。 更新丟失(Lost update) 兩個事務都同時更新一行數據,但是第二個事務卻中途失敗退出,導致對數據的兩個修改都失效了。這是因為系統沒有執行任何的鎖操作,因此併發事務並沒有被隔離開來。 臟讀(Dirty Re ...
  • 1.in後條件不多,可以考慮主表建索引,或用union all 代替 2. in 和 exists的區別: 如果子查詢得出的結果集記錄較少,主查詢中的表較大且又有索引時應該用in, 反之如果外層的主查詢記錄較少,子查詢中的表大,又有索引時使用exists。其實我們區分in和exists主要是造成了驅 ...
  • 游標是一種私有的工作區,用於保存SQL語句的執行結果。在執行一條SQL語句時,資料庫伺服器會打開一個工作區,將SQL語句的執行結果保存在這裡。在Oracle資料庫中有兩種形式的游標:隱式游標和顯式游標。隱式游標是由資料庫伺服器定義的,顯式游標是用戶根據需要自己定義的。 隱式游標隱式游標是資料庫伺服器 ...
  • MySQL在5.0.2版本以上開始支持觸發器,觸發器是有某些帶有命令的時間來觸發某些操作,這些事件包括insert語句、delete語句、update語句等。觸發器可以用於記錄對資料庫的操作。 1、創建mysql觸發器: (1)創建具有單個執行語句的觸發器 create trigger 觸發器名稱 ...
  • 1、編寫目的 使用統一的命名和編碼規範,使資料庫命名及編碼風格標準化,以便於閱讀、理解和繼承。 2、適用範圍 本規範適用於公司範圍內所有以ORACLE作為後臺資料庫的應用系統和項目開發工作。 3、對象命名規範 3.1 資料庫和SID 資料庫名定義為系統名+模塊名 ★ 全局資料庫名和常式SID 名要求 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...