索引的一些事一些情

    以前在個人上一篇文章《系統性能優化二三事》中提到了sql的性能優化的問題。由於時間和篇幅的關係,當時並無進行什麼討論,那如今咱們就來討論關於索引的一些事一些情~html

    認識索引
    索引也叫作"鍵(key)",是存儲引擎用於快速找到記錄的一種數據結構。 索引分爲兩種:b-tree索引、hash索引。這兩種索引的實現上的區別是b-tree索引是創建一種叫b+tree的數據結構上面,而hash索引則是用一個hash函數創建一種相似hashmap的數據結構。由於底層結構的關係咱們的b-tree索引支持、範圍查詢、排序、模糊匹配等功能,而hash索引只支持全值匹配查詢。因此咱們平常工做中用到的索引基本都是b-tree索引,而且後面的討論的索引指的就是b-tree索引~。hash索引雖然只支持全值匹配,可是隻要hash函數設計的好,key分佈的均勻,基本上每次查詢的時間複雜度均可以看做是O(1),這性能也是槓槓的~。另外索引不必定是建了就能起效果,索引建的很差的通常的結果是沒有走索引也就是索引沒起效這種結果就是掃全表,可是有些時候你可能會發現走了某些索引的查詢可能會比掃全表更慢。。。這個後面會有具體的例子進行量化分析~。因此咱們知道建索引這麼簡單的一個操做也是個技術活~。
 
      肯定建索引的目標
     建索引容易,可是建好索引不易。因此建索引以前咱們先要了解一下索引是如何工做來幫助咱們的sql實現快速查詢,若是不瞭解其原理的話那咱們就很難決定把表中的哪些列放到咱們的索引和如何決定這些列的順序~。
    其實總的來講 索引幫助咱們的sql實現快速查是在體如今三個方面:
       一、減小服務器須要掃描的數據量。
       二、避免排序和臨時表
       三、將隨機I/O變成順序I/O
   因此當咱們建一個索引以後就能夠圍繞這三個維度來判斷索引的好壞~。另外對於索引的評級是有一個三星標準的。
   一、若是與一個查詢相關的索引行是相鄰的,或者至少足夠靠近的話,那這個索引就能夠被標記上第一顆星。這最小化了必須掃描的索引片的寬度。這句話看字面可能起來有繞,其實簡單一點描述就是指若是數據庫須要掃描的索引行是最少的那就得到了第一顆星~
  二、若是索引行的順序與查詢語句的需求一致,則索引能夠被標記上第二顆星。
  三、若是索引行包含查詢語句中的全部列——那麼索引就能夠被標記上第三顆星。這避免了訪問表的操做,僅訪問索引就能夠了。
    咱們整理一下:
    數據庫須要掃描的索引行是最少 -> 減小服務器須要掃描的數據量
     索引行的順序與查詢語句的需求一致 -> 避免排序和臨時表,將隨機I/O變成順序I/O 
    索引行包含查詢語句中的全部列 -> 減小服務器須要掃描的數據量,將隨機I/O變成順序I/O 
    因此你知道三星標準和三個維度其實說的是一回事兒~。 知道建索引的目標以後,那下面咱們來學習一下如何建索引。
 
    如何建索引
    這裏討論建的索引指的是b-tree索引而且咱們指定數據庫是mysql,存儲引擎是innodb。
    

    b-tree索引的結構如上圖所示。從結構上咱們知道b-tree索引是一個樹狀的數據結構結點間相互鏈接,同一層的結點從左到右按value的值排序好,其實這種數據結構就叫b+tree。這個b+tree中文的意思就叫平衡樹的意思。叫平衡樹的緣由,由於從樹的根結點到任意一個葉子通過的路徑長度都是相等的~。對於b+ree這種搜索樹的結構來講其實樹越矮越胖是越好的,由於在這些結構中查詢數據的時間複雜度實際上是與樹的高度成正比的~,原理和咱們的二叉搜索樹是同樣的。正由於b-tree索引由於數據是排好序的因此它支持模糊匹配,範圍查詢,排序,group by 等高級操做~。
mysql

   若是有一個情景是讓咱們從user表中查詢名字叫"jack"的年齡是25歲的男性,那應該是怎麼去建一個b-tree索引?。sql

   若是是我通常會建一個(name,age,sex)的b-tree複合索引,why?。由於複合索引有一個最左匹配原則,咱們sql的過濾條件都是先按最左邊的過濾,若是索引的前面的列不能過濾那麼後面的列也是不能過濾的這個道理我相信你們都理解。而咱們的用戶查詢中經常使用的一個查詢就是根據名字查詢,因此name要放在第一列,至於第二列爲何放的是age呢,實際上是從咱們上面的三個維度和三星原則出發的。由於name一般是sex是強相關的,好比在西方國家一個叫jack的人一般能夠確定是個man而不會是個miss,這個在東方國家也是適用的,你能想像一個男人起個名字叫翠花麼。。。而且由於人口基數衆多,同名的人也多。因此name,sex放在前面兩列是起不了多大的過濾效果。這就不符合咱們的"減小服務器須要掃描的數據量"和三星標準的第一標準,可是age是能夠的,而且若是是按年排序的話,age第二列就已經排好序了,不經意間咱們就建立了一個二星索引,因此把age第二行!。sex第三列則是用於濾精確結果。若是查詢只要求返回name,age,sex三列那咱們都不須要去查表裏面的數據,直接返回索引裏面的數據就能夠了,這就是一個perfect的三星索引~。
數據庫

      下面列舉一些經常使用建索引的最佳實踐:緩存

      一、通常建索引時優先建立複合索引而不是多個單列索引,好比經常使用的幾個過濾條件是A,B,C。那就應該建立一個(A,B,C)同時包含三列的複合索引由於這樣的過濾效果最好,而不是分別建A,B,C三個單獨索引。由於mysql在一個查詢語句中只能選擇生效一個索引~。若是建立單列索引的話,好比生效了(A)索引,B,C列的過濾就只能是讀取表中的記錄以後再進行過濾,若是是(A,B,C)複合索引的話就直接在索引就能夠過濾好記錄,大大的減小了服務器的讀取數量~。性能優化

      二、複合索引通常是經常使用的過濾項放在靠前的位置,兩個強相關的列不要放在緊挨在一塊兒,這個上面的jack例子就有說明。服務器

      三、通常來講優先選擇數字的列和字段較小的列建索引,選擇數字的列是由於cpu天生就支持數字的比較,運算複雜度看做是O(1),若是是varchar(n)的話就要一個個字符去比較,那平均複雜度就是O(n/2)了。選擇字段較小的列是由於索引也是佔空間,若是索引太大不放進內存裏面那每次讀索引都要進行一次磁盤的讀取,這個就很影響性能了。數據結構

     

      順序IO、隨機IO與索引函數

     上面提到 "索引建的很差的通常的結果是沒有走索引也就是索引沒起效這種結果就是掃全表,可是有些時候你可能會發現走了某些索引的查詢可能會比掃全表更慢"。why?這就須要分析順序IO與隨機IO的區別了。順序IO指的磁盤沿着扇區一直掃,好比磁盤的讀取速度是40M/S,一條記錄的大小是400Byte。那讀取一條記錄的時間是0.01ms。而一次隨機IO理想的估算是大概是10ms左右,分析以下所示。post

             

 

      好比從一個10W條記錄裏面讀取2000條記錄若是是掃全表那花的時間是

      10ms + 100000 * 0.01ms ~= 1s

       若是是隨機I/O:

       2000 * 10ms = 20s;

       因此若是你的建的索引並無把隨機I/O變成順序I/O那就可能會出現上面的這種狀況了。不過有時候也沒必要要太擔憂,由於db都有一個叫優化器的東西,優化器每每可以分析出這種成本,幫你選擇一種合理的執行方式。       

       

      索引是惟一提高查詢速度的因素嗎?

       這個固然不是了。在系統性能優化二三事》中咱們提到緩存是一種性能優化的方式~。其實這在db查詢也是同樣的道理,好比mysql服務器會預加載索引和錶行數據進到緩存裏面,若是緩存已經有了須要的數據,那就不須要讀一次磁盤,而咱們的磁盤也有一層緩存,若是磁盤緩存裏面有須要的數據,也不須要進行一次物理文件的讀取。因此緩存的設置也是很重要的。

      索引的誤區和注意的地方。

      一、索引的層級不要超過5層。這條是常見的索引規範,其實這條的規範是有必定的適用範圍的,並非絕對的適用。由於提出這條規範的時候當時的計算機的內存仍是很是的小,內存能放的東西很是的有限。若是索引的層級太多,那內存就可能放不下索引,這樣讀索引就要讀一次磁盤,這性能就不好。但如今的計算機內存的容量比當時已經增大了成千上百倍,即便時是超過5層的索引也是能夠放到內存裏面。索引的層數越多,那過濾的效果就越好,實際讀取的表數據就越好。

    二、單表的索引數不要超過六個。這個其實也是有適用範圍的。提出這個規範的緣由一方面是上面提到內存問題,另外一個是表頻繁更新問題,由於若是更新表的索引列索引也是更新的,索引的更新最終也會寫回到磁盤。這就會增長磁盤的負載,影響整個數據庫的性能。但若是這個表是不怎麼更新的呢,更新頻率很低或者不存在瞬間密集的更新,其實創建超過六個的索引也是能夠的。

     其實索引還有不少東西能夠講,不過要講清楚的話恐怕還要個十篇八篇才行。。。如今就暫說到這~。後面有時間再陸續分享。    

     閱讀容易,碼字不易。若是你喜歡個人文章或者以爲有所收穫就請你點下關注或者推薦吧。這樣做者才更加有寫下去的動力~。

相關文章
相關標籤/搜索