MySQL索引原理剖析及優化建議

索引是什么?

??MySQL中的索引也是一種為了便于查找數據而在數據之外創建的一種數據結構,通過這種數據結構我們可以方便得根據條件查找我們想要得數據,索引的目的是加快數據的查找速度,從而提升查詢的效率。索引的作用就類似于新畫字典中的拼音目錄,我們通過拼音可以快速的查找到指定的字。換句話說,如果給你一部沒有拼音目錄的字典,那么我們只能在字典中一頁一頁的去翻(當然,如果字典中的每個字都是按拼音排序,咱也可以通過二分查找來實現),總而言之就是查找效率會非常低下了。因此我們總結一下,索引有以下幾個特點:

優點
??1.加快數據查詢的速度和效率。(相當重要!!!)

缺點
??1.建立索引需要消耗額外的存儲空間。因為索引也是一種按照某種數據結構組織的數據,通過這些數據我們可以快速地定位到我們想要查找的數據。
??2.建立和維護索引需要消耗額外的時間。雖然索引可以提升數據查詢的效率,但是大量的索引的創建和維護的時間成本并不低。
??3.當對表中的數據進行增加、刪除、修改時,除了維護原始數據,索引也需要同時進行動態的維護,降低了數據的維護速度

創建索引的建議

??上面我們對索引的優缺點進行了簡單的分析,那么什么情況下需要創建索引?什么情況下又不適合創建索引呢?我們給出以下幾點建議:
??1. 對于數據體量較大且查詢頻率遠高于變更頻率的列,建議創建索引。
??2. 對于數據區分度不大的列不建議創建索引。比如用戶的性別,枚舉范圍較小的列。
??3. 對于需要頻繁變更(修改和刪除)的列,不建議創建索引。因為在頻繁變更的列上創建索引,會導致變更操作的效率變低。
??4. 數據體量不大或者查詢需求不高的的情況下,不建議創建索引。
??5. 索引需要根據查詢需求來確定,并且盡可能以最小范圍的方式進行創建,不建議對所有列創建索引。對于業務已經變更,需求不大的索引,需要及時drop。

MySQL存儲引擎概覽

??在詳細介紹MySQL索引原理之前,我們需要先簡單介紹一下MySQL的存儲引擎。存儲引擎是處理不同表類型的SQL操作的MySQL組件,目前InnoDB已經成為MySQL默認的存儲引擎,也是目前使用范圍最廣的存儲引擎,除此之外,我們可以使用SHOW ENGINES命令來查看當前版本的MySQL所支持的存儲引擎,示例如下:

mysql> SHOW ENGINES
*************************** 1. row ***************************
      Engine: ARCHIVE
     Support: YES
     Comment: Archive storage engine
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 2. row ***************************
      Engine: BLACKHOLE
     Support: YES
     Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 3. row ***************************
      Engine: MRG_MYISAM
     Support: YES
     Comment: Collection of identical MyISAM tables
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 4. row ***************************
      Engine: FEDERATED
     Support: NO
     Comment: Federated MySQL storage engine
Transactions: NULL
          XA: NULL
  Savepoints: NULL
*************************** 5. row ***************************
      Engine: MyISAM
     Support: YES
     Comment: MyISAM storage engine
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 6. row ***************************
      Engine: PERFORMANCE_SCHEMA
     Support: YES
     Comment: Performance Schema
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 7. row ***************************
      Engine: InnoDB
     Support: DEFAULT
     Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
          XA: YES
  Savepoints: YES
*************************** 8. row ***************************
      Engine: MEMORY
     Support: YES
     Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 9. row ***************************
      Engine: CSV
     Support: YES
     Comment: CSV storage engine
Transactions: NO
          XA: NO
  Savepoints: NO

??由于很多存儲引擎只針對特定的場景,很多人可能終其一生也不會用到,下面就從官網扒拉來一份幾種常見的MySQL特性對比,如下表所示:

Feature MyISAM Memory InnoDB Archive NDB
B-tree indexes Yes Yes Yes No No
Backup/point-in-time recovery Yes Yes Yes Yes Yes
Cluster database support No No No No Yes
Clustered indexes No No Yes No No
Compressed data Yes No Yes Yes No
Data caches No N/A Yes No Yes
Encrypted data Yes Yes Yes Yes Yes
Foreign key support No No Yes No Yes
Full-text search indexes Yes No Yes No No
Geospatial data type support Yes No Yes Yes Yes
Geospatial indexing support Yes No Yes No No
Hash indexes No Yes No No Yes
Index caches Yes N/A Yes No Yes
Locking granularity Table Table Row Row Row
MVCC No No Yes No No
Replication support Yes Limited Yes Yes Yes
Storage limits 256TB RAM 64TB None 384EB
T-tree indexes No No No No Yes
Transactions No No Yes No Yes
Update statistics for data dictionary Yes Yes Yes Yes Yes

備注:
??1.在服務器中實現,而不是在存儲引擎中。
??2.只有在使用壓縮行格式時,才支持壓縮的MyISAM表。使用MyISAM壓縮行格式的表是只讀??的。
??3.在服務器端通過加密功能來實現。
??4.在服務器端通過加密功能來實現;MySQL 5.7及以上版本支持靜態數據加密
??5.在MySQL集群NDB 7.3及更高版本中支持使用外鍵。
??6.在MySQL 5.6及更高版本中支持FULLTEXT索引可用。
??7.在MySQL 5.7及更高版本中支持geospatial 索引可用。
??8.InnoDB內部利用哈希索引實現自適應哈希索引特性。

MySQL索引的分類

??MySQL中的索引,從作用范圍上可以將其分成分單列索引和組合索引。單列索引即一個索引只包含單個列,一個表可以有多個單列索引。組合索引即一個索引包含多個列。這里我們不對單列索引和組合索引做過多介紹。我們主要從不同的用途出發歸納MySQL的索引分類,MySQL中的索引根據其具體的用途在邏輯上主要分成分為以下 3 類:

1.常規索引

??常規索引是 MySQL 中最基本的索引類型,它沒有任何特殊限制限制,其主要目的就是提升數據的查詢效率。常規索引又可以根據其約束和限制的嚴苛程度分為3種,分別是:普通索引、唯一索引和主鍵索引。

普通索引:普通索引允許在索引的列中插入重復值和空值。
唯一索引:唯一索引與普通索引的不同的是創建唯一性索引的目的不僅僅是為了提高訪問速度,同時還要避免數據出現重復。唯一索引的列值必須唯一,但是允許有空值。如果是組合索引,則這些列值的組合的值必須唯一(組合索引不要某個列的值必須唯一)。
主鍵索引:主鍵索引是一種特殊的唯一索引,該索引是專門為主鍵字段創建的索引,不允許值重復或者為空值。

2. 全文索引

??首先全文檢索并不是MySQL最擅長的任務之一,如果有巨大的全文檢索需求,建議使用專門的全文檢索引擎,如Solr和ES等。但是MySQL使用全文索引提供了對全文檢索的支持,全文索引主要用來查找文本中的關鍵字,只能在 CHAR、VARCHAR 或 TEXT 類型的列上創建。全文索引允許在索引列中插入重復值和空值。

3. 空間索引

??與全文索引類似,MySQL中的空間索引也是一種針對特定類型的數據和查詢需求創建的索引, 空間索引主要是對空間數據類型的字段建立的索引,如GIS中常用的坐標數據(經度、維度),創建空間索引的列必須將其聲明為 NOT NULL,空間索引主要用于地理空間數據類型 GEOMETRY,對于沒有這種空間數據處理任務的人來說,這類索引很少會用到。我們當初在一個時空網絡的項目中使用到了空間索引,在針對GPS數據查詢和坐標點距離排序這塊,空間索引有著常規索引無法比擬的優勢。

MySQL索引的實現原理

??上文中我們提供過,MySQL中的索引根據用途和場景不同,主要可以分成三種:常規索引、全文索引和空間索引。不同索引的實現原理不同,且在不同的存儲引擎上的實現方式也不盡相同。比如常規索引的在InnoDB上可以使用B-Tree索引的方式實現,也可以使用Hash索引的方式實現;全文索引可以使用倒排索引的方式來實現;空間索引可以基于R-Tree的方式來實現。這里我們主要針對MySQL中Hash和B-Tree索引的實現原理進行詳細的介紹。

Hash索引原理

??Hash索引也稱為哈希索引或散列索引。MySQL 目前在 MEMORY和NDB 存儲引擎中都支持支持該種類型的索引,其中,MEMORY 存儲引擎將 Hash當成默認索引。Hash索引不是基于樹形的數據結構查找數據,而是根據索引列對應的哈希值的方法獲取記錄所在的位置。Hash索引的原理很簡單,大致如下圖所示,主要是維護一個Hash函數,插入數據的時候對索引列的值進行Hash,得到HashCode后將該行數據存儲在指定位置;查詢數據的時候,也是同樣的操作,先對索引列的值進行Hash,得到HashCode后去到指定的位置讀取當前行的數據。Hash索引的最大優點是訪問速度快,但也存在下面的一些缺點:

??1. 使用Hash索引需要在查詢和建立索引的過程中進行Hash計算,Hash計算是一個比較耗時的操作,相對于 B-樹索引來說,建立哈希索引會耗費更多的時間。
??2. 由于Hash的無序性,無法使用 Hash索引進行排序和范圍查找。
??3. Hash索引只支持等值比較。
??4. Hash索引不支持鍵的部分匹配,因為Hash值是通過整個索引值計算得到的。
??5.隨著數據量的增大,Hash沖突的現象會越來越明顯,此時會出現明顯的性能下降現象。

Hash索引
B+Tree索引原理

B+Tree索引是目前MySQL中使用最廣的索引,MySQL中的B+Tree索引根據其實現方式的不同又可以分為聚集索引和非聚集索引。其中InnoDB存儲引擎中的B+Tree的實現即為聚集索引,聚集索引的B+Tree中的葉子節點存放的是整張表的行記錄數據。而非聚集索引與聚集索引的主要區別在于非聚集索引的葉子節點并不包含行記錄的全部數據,而是存儲相應行數據的主鍵,MySQL中的MyISAM存儲引擎中的B+樹的實現就是非聚集索引。這里主要針對InnoDB存儲引擎中的B+Tree實現原理進行展示,不同的存儲引擎中,B+樹的實現方式也不一樣。B+樹索引是一個典型的數據結構,其包含的組件主要有以下幾個:

根節點:一個 B+樹索引只有一個根節點,根節點位于樹的最頂端。
分支節點:包含的條目指向索引里其他的分支節點。
葉子節點:包含的條目直接指向表里的數據行。B+樹中葉子節點之間通過指針彼此相連。

??基于這種樹形數據結構,表中的每一行都會在索引上有一個對應值。因此,在表中進行數據查詢時,可以根據索引值一步一步定位到數據所在的行。

image.png

??B+樹索引可以進行全鍵值、鍵值范圍和鍵值前綴查詢,也可以對查詢結果進行 ORDER BY 排序。但 B+樹索引必須遵循左邊前綴原則,要考慮以下幾點約束:

查詢必須從索引的最左邊的列開始。
查詢不能跳過某一索引列,必須按照從左到右的順序進行匹配。
存儲引擎不能使用索引中范圍條件右邊的列。

MySQL索引優化建議

??這里給出一些MySQL的索引優化建議,僅供在使用MySQL中作為參考。

1.優先選擇唯一性索引,盡量選擇區分度高的列作為索引,可以更快速的通過該索引來確定某條記錄。
2.為經常作為查詢條件的字段建立索引,為經常需要排序、分組和聯合操作的字段建立索引。
3.限制索引的數目: 越多的索引,會使更新表變得很浪費時間;盡量在當前索引的基礎上擴展索引,盡量不要新建索引;刪除不再使用或者很少使用的索引。
4.盡量使用字段長度較短的列做索引,如果索引的值很長,那么查詢的速度會受到影響。
5.如果索引的值很長,盡量使用前綴來索引,比如like只有在‘%’不在最左邊的時候索引才會生效,也就是最左匹配原則。
6.最左前綴匹配原則,非常重要的原則。
7.索引列不能參與計算,保持列“干凈”:帶函數的查詢不參與索引。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容