如何發現及替換不合適的索引

這是數據庫索引相關內容的第五篇
複製代碼

發現不合適的索引

觸發咱們考慮考慮索引是否合適的契機有兩種數據庫

一種是:生產環境中出現查詢慢,咱們急於解決現實遇到的問題;

一種是:在設計實現階段,咱們但願提早發現設計不合理的索引,以避免後續發佈之後纔出現性能問題;
複製代碼


對於 \color{red}{第一種} 狀況,咱們能夠經過提問來反思如何改進索引。post

1. 是否全部where子句中的全部列都在索引中了?性能

      若是沒有,則添加到索引中,將索引變成半寬索引優化

2. 是否將全部涉及的列都加入到索引中了?spa

      若是變成半寬索引後,仍是沒有解決到性能問題,那麼下一個選擇就是將查詢中全部涉及的列都加入到索引中,造成寬索引;這樣,優化器的訪問只需訪問索引,避免了表訪問。設計

3. 你須要最佳索引code

      若是上述兩種方式,仍未解決性能問題,則要參考《什麼是最好的索引》一文,好好考慮一下索引的設計了索引

      例如:SELECT A FROM XXX WHERE B = 1 AND C = 2;而索引是(A, B, C) 按照《索引》中的優化器邏輯,其索引片是空,即其查詢將進行全索引掃描;若是索引超過10w條記錄,那麼查詢將會很慢get

      可是按照上述的檢查方式,第一條和第二條其都是知足的,可是該索引在必定的數量級下依然會致使查詢效率慢,那麼就要作第三步,對索引進行從新考慮了。io

在正式運行的系統中,建議擴充索引列,而不是新增新的索引或者更換索引列的順序,和將帶來額外的負擔。



對於 \color{red}{第二種} 狀況,咱們能夠經過系統的評估來查看索引是否合適。

1. 統計本地相應時間

      直接先上結論:

      本地響應時間(LRT) = 隨機訪問的數量(TR) * 10ms + 順序訪問數量(TS) * 0.01ms + 有效FETCH數量(F) * 0.1ms

      什麼是隨機訪問:就是一次磁盤IO的時間,約爲10ms
      什麼是順序訪問:一頁包含n行,每行的時間約爲0.01ms;

      再詳細一點:
      DBMS讀取一個索引或一個錶行的成本,即爲一次訪問;
      DBMS掃描索引或表的一個片斷,其中第一行的讀取即爲一次隨機訪問;
      對於後續行的讀取,每行都是一次順序訪問;

      打個比方:
      對於去超市買10個罐頭:
      隨機訪問就比如在超市找到罐頭的貨架的時間,就是10ms
      順序訪問就比如已經找到罐頭的貨架了,只要一個個把罐頭拿下來,每一個罐頭的時間就是0.01ms

      好了,知道了隨機訪問和順序訪問,接下來咱們知道如何肯定隨機訪問和順序訪問的次數

索引訪問次數

      能夠將索引當成一張表,其行數與其包含的表的行數相同,且按照索引鍵值排列

表訪問次數

      咱們假設一次全表掃描將須要一次隨機訪問和N-1次順序訪問


2.舉例

\color{purple}{2.1 主鍵索引}

主鍵索引:select CNAME, CNO, CDESC from table1 where CNO = 221
其中CNO爲主鍵;

索引存儲以下:
111,
112,
113
...
221,
222

那麼優化器是如何檢索的?
i. 根據CNO=221,進行一次隨機訪問,取到221這條索引
ii. 根據221這條索引指向的磁盤位置,經過一次隨機訪問,找到數據塊,獲得CNAME,CDESC

那麼LRT是多少?
很好計算: 兩次隨機訪問 + 1次FETCH = 2 * 10ms + 0.1ms 約等於 20ms


\color{purple}{2.2 聚簇索引}

select CNO, CNAME, CDESC from table1 where CTYPE = 1 and CNAME = 'ZHANG' order by CDESC

假設索引是(CTYPE, CNAME, CDESC) 假設CTYPE =1 和CNAME='ZHANG'能從10w的索引中過濾出1000條

1000條索引以下:
1,'ZHANG', 1
1,'ZHANG', 2
....
1,'ZHANG', 1000

那麼LRT是多少?
首先,一次隨機訪問索引的時間,定位到索引1000條的第一行
其次, 1000次順序訪問索引的時間
而後,因CNO不在索引中,因此還須要經過索引進行磁盤查找;
由於是聚簇索引,因此表的順序和索引的順序是一致的,訪問表的時間和索引是同樣的,即一次隨機訪問表的時間,和1000次順序訪問的時間(999很差計算,咱們都約等於1000)

LRT= 1次索引隨機訪問 + 1000次索引順序訪問 + 1次表隨機訪問 + 1000次表順序訪問 + 1000次FETCH
      = 10ms + 1000 * 0.01ms + 10ms + 1000 * 0.01ms + 1000 * 0.1ms
      = 140ms

\color{purple}{2.3 非聚簇索引}

同2.2 ,若是一樣是該查詢語句,可是索引變成非聚簇索引會怎麼樣?

很顯然,表的存儲會發生變化,再也不是跟索引的順序一致,而且不是連續存儲了;

因此,

LRT = 1次索引隨機訪問 + 1000次索引順序訪問 + 1000次表隨機訪問 + 1000次FETCH
      = 10ms + 1000 * 0.01ms + 1000 * 10ms + 1000 * 0.1ms
      = 10s(約等於)

你看,一樣的1000條索引,查詢速度和2.2相差這麼多!

這個索引該如何優化呢,很顯然,若是它不是聚簇索引,就要將CNO歸入到索引中來,避免表的隨機訪問;將全部的訪問都回到索引內部

\color{red}{CTYPE, CNAME, CDESC,CNO}

很顯然,這就是《什麼索引是好的索引》中介紹的所謂的好的索引,知識都是相輔相成的。

使用了該更改後的索引,其LRT會變成多少?

LRT = 10ms + 1000 * 0.01ms + 1000 * 0.1ms = 120ms

速度提高了100倍!



好了,本文重點介紹瞭如何在實際生產環境中和設計過程當中發現問題及優化索引,只要掌握原則,瞭解思路,剩下的就是運用了。


其餘相關章節
複製代碼

數據庫索引相關文章之一:《B樹,一點都不神祕》
數據庫索引相關文章之二:《B樹很簡單,插入so easy》
數據庫索引相關文章之三:《索引》
數據庫索引相關文章之四:《什麼索引算是好的索引》
數據庫索引相關文章之五:《如何發現及替換不合適的索引》
數據庫索引相關文章之六:《索引總結》

相關文章
相關標籤/搜索