1. 程式人生 > 其它 >Mysql高效能的祕密 - 深入理解索引

Mysql高效能的祕密 - 深入理解索引

索引的目的在於提高查詢效率,與我們查閱圖書所用的目錄是一個道理:先定位到章,然後定位到該章下的一個小節,然後找到頁數。相似的例子還有:查字典,查火車車次,飛機航班等。即通過不斷地縮小想要獲取資料的範圍的方式來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是說,有了這種索引機制,我們可以總是用同一種查詢方式來鎖定資料。

Mysql索引

引言

眾所周知,Mysql的高效能與索引有著密不可分的關係:

  • 正確的建立和使用索引是實現高效能查詢的基礎。
  • 索引是對查詢效能優化最有效的手段。

那麼正確建立和使用索引的姿勢什麼樣的呢,以及如何優化查詢效能呢,本文將進行系統性講解。

一、建立高效能的索引

  • 1.1 索引型別

    • B-Tree

      • 特點
        • 所有值按順序存放。
        • 每一個葉子頁到根的距離相同
        • 葉子節點儲存了指向資料的指標
        • 葉子節點與葉子節點通過指標連結,方便範圍查詢
        • 樹的深度與表的大小直接相關
        • 索引對多個列排序的依據是,create table語句中定義索引時列的順序
      • 適用場景
        • 全值匹配
        • 列字首查詢
          • 即xx*,注意與最左字首不同
        • 最左字首
          • 注意:跳列無效,只能匹配第一列
        • 只訪問索引列的查詢
          • 又叫“覆蓋索引”
        • 鍵值範圍
        • 精確匹配某一列並範圍匹配另外一列
          • 注意:此時範圍查詢右邊的列無法使用索引
      • 最佳實踐
        • 如果範圍查詢列值有限,可以通過使用多個等於條件代替範圍條件,以提高效率。
        • 可以使用相同的列但順序不同的索引來滿足不同型別的查詢需求。
    • Hash index

      • 特點
        • 基於雜湊表實現,只有精確匹配索引所有列的查詢才有效。
        • 速度非常快:對每一行資料,儲存引擎都會對所以列計算一個雜湊碼(雜湊碼是一個較小的值,儘可能少的重複)。
        • 行讀取:雜湊索引僅存雜湊值和資料行的指標,不儲存欄位值,故無法避免讀取行。
        • 不支援排序:雜湊表按照槽(雜湊碼)順序儲存,不以值(資料行)的順序儲存。
        • 缺點
          • 僅僅支援等值比較查詢(=、IN、<=>),不支援範圍查詢。
          • 衝突較多時維護成本較高。
      • 適用場景
        • Memory引擎表的預設儲存引擎
          • 與眾不同的是,Memory支援非唯一的雜湊索引,如果雜湊碼相同,索引會以列表方式存放多個記錄指標到同一個雜湊條目中。
        • InnoDB的“自適應雜湊索引”
          • 當某些索引使用很頻繁時,會在記憶體中基於B-Tree索引之上在建立一個雜湊索引。
        • 如儲存URL這種長度較長,可辨別度較高的資料時。
      • 最佳實踐
        • 如果儲存引擎不支援雜湊索引,則可以建立自定義雜湊索引
        • 雜湊函式不要使用SHA1()和MD5(),因為其雜湊結果很長,浪費大量空間,且較慢
        • 可使用簡單雜湊函式crc32(),衝突範圍可接受同時又能提供更好的效能。
          • 資料量非常大時,crc32()會出現大量雜湊衝突,可考慮自實現64位雜湊函式。簡單的辦法可以使用MD5()函式返回值的一部分作為自定義雜湊函式
        • 根據“生日悖論”,出現雜湊衝突的概率增長速度可能比想象快的多。查詢時,為避免衝突問題,where條件中可帶入雜湊值和對應列值。
    • R-Tree空間索引

      • MyISAM
    • 全文索引

  • 1.2 索引的優點

    • 索引有以下幾個方面的好吃:

      • 1.因為索引可以快速定位到表中指定位置,故大大減少了伺服器需要掃描的資料量。
      • 2.索引按順序儲存,故可以幫助伺服器避免排序和臨時表。
      • 3.索引可以將隨機I/O變為順序I/O。
    • 《Relational Database Index Design and the Optimizers》--作者:Leach/Lahdenmaki

    • “三星系統”:

      • 衡量一個索引是否適合某個查詢
        • 1.索引將相關記錄放在一起(資料頁),則獲得一星
        • 2.索引中的資料順序與查詢的排列順序一致,則獲得二星
        • 3.索引中的列包含了查詢中所需的全部列則,則獲得三星
    • 索引的限制:

      • 只有索引的好處大於其帶來的額外工作時,索引再有必要。
        • 1.對於非常小的表,簡單的全表掃描更高校。
        • 2.對於中到大型的表,索引非常有效。
        • 3.對於特大型的表,建立和使用索引的代價也較大。此時不能一條一條記錄匹配,而需要區分出直接查詢需要的一組資料,例如使用分割槽技術。
  • 1.3 高效能的索引策略

    • 1.必須是“獨立的列”,否則不會使用索引

      • 如果是表示式中的一部分,則不是獨立的列。
      • 如果是函式的引數,則怒視獨立的列。
    • 2.字首索引

      • 概念:有時候索引很長的字元列會讓索引變得大且滿。通常可以索引開始的部分字元,在提高效率的同時可大大節約索引空間。
      • 字首索引會降低索引的選擇性。
      • 對於BLOB、TEXT或者很長VARCHAR型別的列,必須使用字首索引!
      • 合適的長度的訣竅:選擇足夠長的字首以保證較高的選擇性,同時又不能太長以便節約空間。
      • 計算合適的長度:
        • 使字首的選擇性接近於完整列的可選擇性。
      • 字首函式:LEFT(calume,len)
    • 3.多列索引

      • 反面教材:在所有欄位上建立單列索引
        • 5.0版本前:效能不如全表掃描
        • 5.0及其後:會在單列索引基礎上執行合併策略,但浪費cpu、mem等資源,尤其大資料量時
      • 當explain的Extra出現索引合併時,說明此時需要一個多列索引
    • 4.選擇合適的索引列順序

      • 1.正確的索引順序依賴於使用該索引的查詢的順序,並且需要同時考慮如何更好的滿足排序和分組的需要。
      • 2 在多列b-tree索引中,索引的順序:先按照最左邊的列排序,然後是第二列,等等。
      • 3 效能不僅與索引列的可選擇性(基數)有關,也和值的分佈有關。
      • 4 最佳實踐:
        • 不考慮排序和分組情況下,可將選擇性最高的列放到索引最前列。
        • 根據執行頻率最高的查詢來調整索引列的順序,這種情況下索引的可選擇性更高。
          • 索引列順序:sarg查詢預測
      • 5 使用“三星索引”衡量
    • 5.clustered聚簇索引

      • 並不是一種單獨的索引型別,而是一種資料儲存方式。
      • InnoDB的聚簇索引在同一個結構中同時儲存了B-Tree索引和資料行。即: 在InnoDB中聚簇索引就相當於“表”。
      • 術語”聚簇“表示資料行和相臨的鍵值緊湊地儲存在一起。
        • 節點頁只包含索引列
        • 葉子頁包含了行的全部資料
      • 一個表只能有一個聚簇索引。
        • InnoDB通過主鍵聚集資料,即在主鍵上建立聚簇索引。如果沒有定義主鍵,則會選擇一個唯一的非空索引代替,如果沒有這樣的索引,則會隱式的定一個主鍵來作為聚簇索引。
      • 優點:
        • 減少了磁碟I/O:將相關資料儲存在了一起,僅需查詢少量資料頁,減少了磁碟I/O。
        • 資料訪問更快:聚簇索引將索引和資料儲存在同一個b-tree結構中,故查詢速度比非聚簇索引要快!
      • 缺點:
        • 插入速度嚴重依賴於插入順序:按照主鍵的順序插入是最快的方式。
        • 行移動:更新的代價較高,innodb強制將被更新的行移動到新的位置。
        • 頁分裂:向已經滿的資料頁插入資料,或主鍵被更新導致需要移動行時,可能有“頁分裂”問題。
        • 二級索引(非聚簇索引)可能比想象中的要更大!
          • 二級索引葉子結點儲存的不是指向行的物理位置的指標,而是行的主鍵值。
          • 使用主鍵值當作指標比使用物理行指標佔用更大的空間,但避免了行移動和頁分裂時的維護工作!
        • 二級索引訪問需要兩次索引查詢。
          • 第一次查詢到主鍵值。第二次根據主鍵值查詢聚簇索引!
        • “間隙鎖”問題:高併發插入場景下,在innoDB中按主鍵順序插入,主鍵的上界會成為“熱點”,故併發插入時可能會出現“間隙鎖”問題!
    • 6.覆蓋索引

      • 如果一個索引包含(或覆蓋)所有查詢需要的欄位的值,就成為覆蓋索引。
      • 覆蓋索引只需要掃描索引,不需要回表查詢,能極大的提升效能。
      • 好處:
        • 1.極大的減少資料訪問量。
        • 2.索引按照列值順序儲存,io密集型的範圍查詢比隨機磁碟IO讀取小的多。
        • 3.由於InnoDB的聚簇索引,覆蓋索引對InnoDB表特別有用。
      • 注:當使用覆蓋索引時,從的EXPLAIN 的extra列可以看到“Using index”的資訊。
    • 7.使用索引掃描來做排序

      • Mysql有兩種方式可以生成有序的結果,通過排序操作;或者按照索引順序掃描。
      • EXPLAIN的type列為“index”時,說明mysql使用列索引掃描來做了排序。
      • 只有當索引的列順序和order by子句的順序完全一致,並且所有列的排序方向(正序/倒序)相同時,才能使用索引對結果進行排序。
      • 同樣需要滿足“最左字首要求”。
        例外:前導列為常量,可以不滿足“最左字首要求”:如果where子句或order by子句對最左列指定為常量,則可以使用索引排序。
    • 8.壓縮索引(字首壓縮)

      • MyISAM使用字首壓縮來減少索引大小,從而讓更多索引可以放入到記憶體中,在某些情況下能極大提高效能。
        • 預設只壓縮字串,可通過引數設定調整。
      • MyISAM壓縮方法:先完全儲存索引塊中的第一個值,然後將其他值與第一個值比較得到相同字首的位元組數和不同字尾部分,然後把這部分儲存起來即可。
        • eg:索引塊中的第一個值為“perform”,第二個值為“performance”,壓縮後的儲存是為“7,ance”這種形式。
      • 壓縮塊使用更少的空間,代價是某些操作可能更慢
      • 適用於I/O密集型應用,不適用於CPU密集型應用
    • 9.冗餘和重複索引

      • 重複索引是指在相同的列上,按照相同的順序,建立相同型別的索引。
        • 應避免,發現後應該立即移除。
      • 冗餘索引與重複索引有一些不同。如果建立列索引(A,B),再建立索引(A)就是冗餘索引,因為這只是前一個索引的字首索引。但再建立索引(B,A)就不是冗餘索引。
      • 大多數情況下應該擴充套件已有的索引而不是建立新索引,但也有時候出於效能方面考慮需要冗餘索引!
    • 10.索引和列

      • InnoDB只有在訪問行的時候才會對其加鎖,而索引能減少InnoDB訪問的行數,從而減少鎖的數量。
      • 進而減少鎖定行,可以降低額外的開銷,即使很小;鎖定超過需要的行會增加鎖徵用並減少併發行。
  • 1.4 拓展:

    • 【InnoDB和MyISAM的資料分別對比】
    • 【在InnoDB表中按主鍵順序插入行】
  • 1.5 最佳實踐

    • 建立索引時,一需要考慮列的可選擇行(高的),二考慮列的使用頻率。

    • 設計索引時,不僅只為現有查詢考慮需要哪些索引,還要考慮對查詢進行優化。應該同時優化查詢和索引以找到最佳的平衡,而不是閉門造車設計最完美的索引。

    • 為了是查詢走索引,可以使用IN()語法強制查詢走索引。

      • 但是IN()也不能濫用,因為每額外增加一個IN條件,優化器需要做的組合都是以指數形式增加,最終可能會極大的降低查詢效能。
    • 儘可能將需要做範圍查詢的列放在索引的後面,以便優化器能儘可能多的使用索引列。

    • 避免多個範圍條件:對於範圍條件查詢,Mysql無法在使用範圍列後面的其他索引列了,但是對於“多個等值條件查詢”則沒有這個限制。

二、查詢效能優化

待續