1、索引基礎html
1. B-Tree索引mysql
<1> 全部的值都是按順序存儲的,而且每個葉子頁到根的距離相同。算法
<2> 順序組織存儲,很適合查找範圍數據,效率會很是高。sql
<3> 能夠有效使用B-Tree索引的查詢:全值匹配、匹配最左前綴、匹配列前綴、匹配範圍值、精確匹配某一列並範圍匹配另外一列、只訪問索引的查詢,還能夠用於查詢中的order by和group by操做。數據庫
<4> B-Tree索引的限制:緩存
若是不是按照索引的最左列開始查找,則沒法使用索引;服務器
不能跳過索引中的列;數據結構
若是查詢中有某個列的查詢範圍,則其右邊全部列都沒法使用索引優化查詢。併發
2. 哈希索引函數
<1> 基於哈希表實現,只對精確匹配索引全部列的查詢纔有效。
<2> 對於每一行數據,存儲引擎都會對全部的索引列計算一個哈希碼,將全部的哈希碼存儲在索引中,同時在哈希表中保存指向每一個數據行的指針。
<3> 在mysql中只有memory引擎顯式支持哈希索引。Memory引擎支持非惟一哈希索引,若是多個哈希列的哈希值相同,索引會以鏈表的方式存放多個記錄指針到同一個哈希條目中。
<4> 哈希索引只需存儲哈希值和對應的行數據地址指針,因此索引的結構十分緊湊,使得哈希索引查找的速度很是快。
<5> 哈希索引限制:
哈希索引只包含哈希值和行指針,而不存儲字段值,因此不能使用索引中的值來避免讀取行,即沒法使用哈希索引進行索引覆蓋查詢。
哈希索引數據並非按照索引值順序存儲的,因此沒法用於排序。
不支持部分索引列匹配查找,由於哈希索引始終是使用索引列的所有內容來計算哈希值的。
只支持等值比較查詢(=、IN、<=>),不支持任何範圍查詢。
出現哈希衝突時須要遍歷鏈表中全部的行指針,逐行進行比較直到找到全部符合條件的行。衝突不少的話一些索引維護操做的代價也會很高。
3. 建立自定義哈希索引
若是存儲引擎不支持哈希索引,能夠在B-Tree索引基礎上建立一個僞哈希索引。仍是使用B-Tree進行查找,可是使用哈希值而不是鍵自己進行索引查找。這在字段值是很長的字符串的時候,能夠很好地提高性能。
能夠選擇CRC32或FNV64做爲哈希函數(哈希值爲整數),不要使用SHA1和MD5,由於這兩個函數產生的哈希值是很是長的字符串。
可使用觸發器來維護哈希值。
爲避免哈希衝突,使用哈希索引進行查詢的時候,必須在where條件中帶入哈希值和對應列的值。
2、索引優勢
1. 大大減小了服務器須要掃描的數據量。
2. 能夠幫助服務器避免排序和臨時表。
3. 能夠將隨機I/O變爲順序I/O。
評價一個索引是否適合某個查詢的「三星系統」:索引將相關的記錄放到一塊兒;索引中的數據順序和查詢中的排列順序一致;索引中的列包含了查詢中須要的所有列。
然而,索引並不老是最好的解決方案,只有當索引幫助存儲引擎快速查找到記錄帶來的好處大於其帶來的額外工做時索引纔是有效的。對於很是小的表,大部分狀況下簡單的全表掃描更高效;對於中到大型表,索引就很是有效;但對於特大型表,創建和使用索引的代價增大,須要考慮使用分區、塊級別元數據技術等其餘技術。
3、高性能的索引策略
1. 獨立的列
若是查詢中的列不是獨立的,mysql就不會使用索引,「獨立的列」是指索引列不能是表達式的一部分,也不能是函數的參數。
2. 前綴索引和索引選擇性
<1> 前綴索引能夠節約索引空間,提升索引效率。
<2> 使用前綴索引會下降索引的選擇性(不重複的索引值和數據表記錄總數的比值,索引的選擇性越高則查詢效率越高,惟一索引的選擇性是1,這是性能最好的),因此要選擇足夠長的前綴,但又不能太長,太長會佔用太多存儲空間,因此須要對最多見值列表進行分析來決定前綴的合適長度。能夠將平均選擇性:Select count(distinct left(name,length))/count(*) from ...; 與完整列選擇性進行對比得出最適合的前綴長度。另外,數據分佈不均勻時也須要考慮最壞的狀況。
<3> 前綴索引沒法使用於order by和group by,也沒法作覆蓋掃描。
3. 多列索引選擇合適的索引列順序
多列索引的索引列順序選擇一般都要考慮如何更好知足查詢、排序、分組的須要。當不須要考慮排序和分組時,將選擇性最高的列放在前面一般是很好的,固然也須要結合值的分佈考慮,可能須要根據那些運行頻率最高的查詢來調整索引列的順序。
4. 聚簇索引
<1> 當表有聚簇索引時,它的數據行實際上存放在索引的葉子頁中。(葉子頁包含了行的所有列數據,可是節點頁只包含了索引列)
<2> 一個表只能有一個聚簇索引。(覆蓋索引能夠模擬多個聚簇索引的狀況)
<3> 不是全部的存儲引擎都支持聚簇索引。
<4> 一些數據庫服務器容許選擇哪一個索引做爲聚簇索引,但mysql的存儲引擎並不支持。InnoDB經過主鍵彙集數據,如果沒有定義主鍵,則選擇一個惟一的非空索引代替。若是沒有這樣的索引,就隱式定義一個主鍵來做爲聚簇索引。InnoDB只彙集在同一個頁面中的記錄,包含相鄰鍵值的頁面可能會相距甚遠。
<5> 聚簇索引的優勢:
能夠把相關的數據保存在一塊兒。
數據訪問更快。
使用覆蓋索引掃描的查詢能夠直接使用頁節點中的主鍵值。
<6> 聚簇索引缺點:
聚簇數據最大限度地提升了I/O密集型應用的性能,可是若是數據所有都放在內存中,則訪問的順序就沒那麼重要了,聚簇索引也就沒什麼優點了。
插入速度嚴重依賴於插入順序。
更新聚簇索引列的代價很高,由於會強制InnoDB將每一個被更新的列移動到新的位置。
基於聚簇索引的表在插入新行或者主鍵被更新致使須要移動行的時候,可能面臨「頁分裂」的問題,致使表佔用更多的磁盤空間。
聚簇索引可能致使全表掃描變慢,尤爲是行比較稀疏,或者因爲頁分裂致使數據存儲不連續的時候。
二級索引(非聚簇索引)可能要更大,由於在二級索引的葉子節點包含了引用行的主鍵列。
二級索引訪問須要兩次索引查找。(對於InnoDB,自適應哈希索引能減小這樣的重複工做)
InnoDB和MyISAM的數據分佈對比:
<1> MyISAM按照數據插入的順序存儲在磁盤上。
<2> MyISAM中主鍵索引和其餘索引在結構上沒什麼不一樣,主鍵索引就是一個名爲primary的惟一非空索引。
<3> 聚簇索引的每個葉子節點都包含了主鍵、事務ID、用於事務和MVCC的回滾指針以及全部的剩餘列。
<4> InnoDB二級索引的葉子節點存儲的不是「行指針」而是主鍵值,並以此做爲指向行的「指針」。好處:InnoDB在移動行時無須更新二級索引中的這個「指針」,壞處:二級索引佔用更多的空間。
在InnoDB表中按主鍵順序插入行:
使用InnoDB時應該儘量地按主鍵順序插入數據,而且儘量地使用單調增長的聚簇鍵的值來插入新行,不然會致使已經刷到磁盤上的目標頁被從新讀取到內存中、頁分裂、數據碎片等狀況。須要optimize table來重建表並優化頁的填充。
對於高併發工做負載,在InnoDB中按主鍵順序插入可能會形成明顯的爭用,可能須要考慮從新設計表或應用,或者更改innodb_autoinc_lock_mode配置。
5. 覆蓋索引
索引包含了全部須要查詢的字段的值,無須讀取數據行。
覆蓋索引優勢:
<1> MySql會極大地減小數據訪問量。
<2> 對於I/O密集型的範圍查詢會比隨機從磁盤中讀取每一行數據的I/O要少得多。
<3> 一些存儲引擎如MyISAM在內存中只緩存索引,數據則依賴於操做系統來緩存,所以訪問數據須要一次系統調用,使用覆蓋索引就不須要系統調用了。
<4> 若是InnoDB的二級索引爲覆蓋索引,則能夠避免對主鍵索引的二次查詢。
注意:不是全部類型的索引均可以成爲覆蓋索引,也不是全部存儲引擎都支持覆蓋索引。MySql只能使用B-Tree索引作覆蓋索引。
索引覆蓋查詢:
MySQL查詢優化器會在執行查詢前判斷是否有一個索引能進行覆蓋。(就算沒有where子句也可使用索引覆蓋查詢)Explain出來的「extra」列爲「using index」說明查詢使用了覆蓋索引。
使用索引掃描來作排序:
若是Explain出來的「type」列爲「index」說明使用了索引掃描來作排序。按索引順序讀取數據的速度一般要比順序地全表掃描慢,由於若是索引不能覆蓋查詢所需的所有列,每掃描一條索引記錄就要回表查詢一次對應的行,這基本上都是隨機I/O。
MySQL可使用同一個索引既知足排序又用於查找行。用於排序的索引須要知足如下條件:
<1> 索引的列順序和order by子句的順序徹底一致,而且全部列的排序方向都同樣。
<2> 若是查詢須要關聯多張表,則只有當order by子句引用的字段所有爲第一張表時,才能使用索引作排序。
<3> order by子句引用的字段須要知足索引的最左前綴的要求。有一種狀況order by子句能夠不知足索引的最左前綴的要求,就是前導列爲常量的時候,例如where子句或join子句中對這些列指定了常量。
壓縮(前綴壓縮)索引:
MyISAM存儲引擎使用前綴壓縮來減小索引大小,從而讓更多的索引能夠放入內存中,提升性能。具體方法:先徹底保存索引塊中的第一個值,而後將其餘值和第一個值進行比較獲得相同前綴的字節數和剩餘的不一樣後綴部分,把相同前綴的字節數和剩餘的不一樣後綴部分存儲起來便可。
壓縮索引的缺點是:myiSAM查找時沒法在索引塊使用二分查找而只能從頭開始掃描,order by desc時倒序掃描更慢。
能夠在create table語句中指定pack_keys參數來控制索引壓縮的方式。
冗餘和重複索引:
重複索引是指在相同的列上按照相同的順序建立相同類型的索引。MySQL須要單獨維護重複的索引,而且優化器在優化查詢的時候也須要逐個進行考慮,重複索引會影響性能。
MySQL的惟一限制和主鍵限制都是經過索引實現的。
若是建立了索引(A,B),再建立索引(A)就是冗餘索引。不一樣類型的索引不算冗餘。應該儘可能擴展已有的索引而不是建立新索引,除非擴展以後會對性能有很差的影響。
索引和鎖:
<1> 索引可讓查詢鎖定更少的行。
<2> 即便使用了索引,InnoDB也可能鎖住一些不須要的數據。
<3> InnoDB在二級索引上使用共享讀鎖,但訪問主鍵索引須要排它鎖,這消除了使用覆蓋索引的可能性,而且使得select for update比lock in share mode或非鎖定查詢要慢不少。
4、索引優化策略
1. 有些列雖然選擇性低,但若是在where子句中使用頻率很高的話也應該建立索引,或將其做爲多列索引的前綴列。
2. 就算在查詢中沒有某個列的限制,也能夠加上這個列的判斷,這樣mysql才能匹配索引的最左前綴。如:sex in (‘m’,’f’),但也不能濫用,每增長一個In條件優化器須要作的組合都將以指數形式增長,下降查詢性能。
3. Mysql查詢只能使用索引的最左前綴,直到遇到第一個範圍條件列,因此儘量將須要作範圍查詢的列放在索引的後面,以便優化器能使用盡量多的索引列。(用IN來來代替範圍條件)
4.除了or查詢,MySQL查詢每次最多隻使用一個索引,由於相對於全表掃描和只使用一個索引,去分析兩個或兩個以上的索引二叉樹更加耗時。對於查詢:select col from table where col1=** and col2=*** and col3=****,創建複合索引(col1,col2,col3)來進行查詢的性能是最好的。
5、維護索引和表
1. check table:檢查索引和表的錯誤。
2. repair table:修復損壞的表。若是存儲引擎不支持能夠經過一個不作任何操做的alter命令來重建表。
3. analyze table:從新生成索引統計信息。
MySQL的查詢優化器會經過records_in_range()和info()這兩個API來了解存儲引擎的索引值的分佈信息,以決定如何使用索引。每種存儲引擎實現索引統計信息的方式不一樣:Memory引擎不存儲索引統計信息,myisam將索引統計信息存儲在磁盤中,InnoDB經過隨機的索引訪問進行評估並將統計信息存儲在內存中。InnoDB在打開某些information_schema表,或使用show table status和show index,或在mysql客戶端開啓自動補全功能的時候都會觸發索引統計信息的更新。
4. optimize table/導出再導入:減小索引和數據的碎片。若是存儲引擎不支持能夠經過不作任何操做的alter命令來重建表。
最後推薦一篇博文:MySQL索引背後的數據結構及算法原理,這篇博文中結合數據結構和計算機磁盤讀寫原理詳細分析了數據庫系統爲什麼使用Btree做爲索引數據結構,以及MySQL聚簇索引和非聚簇索引的實現,如何高性能使用索引的策略,是我閱讀過寫得最好的文章了,強力推薦!!!