開篇小測驗

下面這樣一個小SQL 你該怎麼樣添加最優索引

兩個表上現在只有聚集索引

bigproduct 表上已經有聚集索引 ProductID

bigtransactionhistory 表上已經有聚集索引 TransactionID

索引的作用及其使用_字段

 

你是否一眼就能看出來呢?

答案將在文章中逐步揭曉~~~

簡單粗暴的添加索引

首先我們看一下沒有優化前的執行計劃

索引的作用及其使用_索引查找_02

 

clustered index scan 這其實就是表掃描,不是table scan 只是因為表上有聚集索引

可以看出這個查詢倆表都使用了表掃描!

where 條件添加索引

首先大多數人都知道 where 條件中的字段需要添加索引! 我們添加一下看看效果創建

在 bigproduct 表上創建 name 列索引,在bigtransactionhistory表上創建TransactionDate 列索引。

再次執行語句看一下效果!

 

索引的作用及其使用_字段_03

 

索引的作用及其使用_聚集索引_04

 

添加where索引以後可以看到以下幾個現象

  • bigproduct 從原來的clustered index scan 變成 index seek
  • 另外多出來個KEY Lookup(clustered)
  • bigproduct 上添加的索引起了作用,邏輯讀bigproduct 由 601 變成10。
  • bigtransactionhistory 沒啥變化啊還是clustered index scan

解釋一下出現的現象 :首先一點bigproduct 邊添加的where條件索引,起到了作用,執行的時候不是全表掃描了,邏輯讀有明顯的下降,出現的 KEY Lookup 是因為選擇(select)的列,在索引中沒有,而需要通過聚集索引再查找一次,再找一次也意味着多一部分開銷!那麼同樣添加了where 條件索引的bigtransactionhistory表為什麼沒起作用呢?那是因為SQL優化器在選擇計劃的時候認為,不使用TransactionDate 列索引查找效率會更好!

真的麼?我們來驗證一下,通過指定選擇索引,來讓優化器選擇索引查找!

索引的作用及其使用_字段_05

 

索引的作用及其使用_聚集索引_06

 強制使用索引以後,可以看出邏輯讀由 14W 變成1961W,語句時間也變得很長,這就是優化器為什麼不選用你加的索引!優化器還是很智能的吧。

高能預警:優化器可不是什麼時候都這麼智能的...由於緩存計劃或優化器抽風等原因,也會出現優化器用了這種索引,導致你的語句奇慢,讀飆升直接影響到你的內存、磁盤、CPU資源!另外如果這樣一條語句是系統中一條很頻繁運行的語句,你的系統就掛了!沒錯就掛了!這就是開篇拋出的問題就是因為一條語句!

消滅Key Lookup 添加select 字段

這就是傳説中的覆蓋索引!

看到執行計劃中存在Key Lookup 而且消耗佔比很高,如上面強制索引的計劃,那麼我們就要想到的 在索引中包含那些SELECT 的列!如果消耗低,邏輯讀少,如上面bigproduct 表中的Key Lookup 就可以忽略(如果你追求完美,也一樣優化就可以了)。

包含列的圖形化創建:@秋仙 特意給你的説明

索引的作用及其使用_索引查找_07

語句創建就是:

索引的作用及其使用_索引查找_08

下面我們添加一下看看效果:

 

索引的作用及其使用_聚集索引_09

 

索引的作用及其使用_索引查找_10

 

添加select 索引字段後可以看出的現象:

  • 優化器自己選擇了index seek
  • bigtransactionhistory佔比最高的Key Lookup消失了
  • 邏輯讀由原來無索引的14W變成1W
  • bigtransactionhistory表還提示缺少索引?

通過優化索引添加select 字段,我們看出語句又一次得到了提升bigtransactionhistory 從表掃描變成索引查找,邏輯讀由14W變成 1W!這是一個質的飛躍啊!

CREATE NONCLUSTERED INDEXTransactionDate包含ProductID_QuantityON[dbo].[bigTransactionHistory] ([TransactionDate])------INCLUDE 就是包含列INCLUDE ([ProductID],[Quantity])GO

那為什麼還提示缺少索引呢?創建一下試試吧!

索引再優化加入表關聯列

按照提示我們創建索引:和上一個索引的不同 ProductID 列由包含列變成了索引列!

索引的作用及其使用_聚集索引_11

 

我們看一下效果:

 

索引的作用及其使用_索引查找_12

 

索引的作用及其使用_索引查找_13

 

再次優化索引以後可以看到以下幾個現象

  • bigtransactionhistory表還是索引查找index seek
  • bigtransactionhistory依然沒有了Key Lookup
  • 兩表關聯的hash join 變成了nested loops
  • 並行計劃變成了串行
  • 邏輯讀又從1W 變成18

又一次質的飛躍!讀從原來的14W 變成1W 又變成18,這樣大大減少了內存和IO的消耗,另外並行計劃也變成了串行,無疑又減少了大量CPU的消耗!語句時間,我想這裏就不用多説了吧?

高能預警:這裏所説的hash join,並行變串行,不懂的朋友可以在百度自行學習,這裏只是針對當前語句的情況,不能一概而論!

精簡你的索引

大家都知道,索引會導致update、insert、delete操作變慢!那麼儘量精簡你的索引就是一個很重要的話題了!

上面的優化過程中我們創建了幾個索引,以bigTransactionHistory為例來看一下:

索引的作用及其使用_字段_14

 

腳本這裏就不貼了,其實我們最後創建的索引 ProductID_TransactionDate包含Quantity 已經包含了前兩個索引,而且可以説無論任何類似語句都使用ProductID_TransactionDate包含Quantity 就可以了!

那麼我們就可以清除前兩個索引!

至此語句的優化算是結束了,留下的就是bigproduct 依然有一個Key Lookup可以優化,可以仿照上面的繼續優化,這裏就不細説了。

這就是索引的重要性!

開篇小測試你做對了麼?如果沒做對那麼這麼請你自行模擬一個場景再現本篇的話題吧!

總結

數據庫整體緩慢往往都是因為索引問題導致的,不要小看一條語句的威力,一條高頻的語句足可以讓你的數據庫徹底無法工作。在和大量的運維人員交流的過程中發現,對於索引的重要性恰恰沒有足夠的重視,一提到優化就是修改SQL語句、讀寫分離、分佈式數據庫等捨近求遠的方案,優化索引才是對數據庫最高性價比的優化。