御麗詩妃談MySQL索引怎麼用

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

追逐仰望星空 2020-08-05 10:43:38

推薦學習

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

爲了能讓索引能有更直觀的效率,我在一張表裏扔進了百萬條數據(光靠這些數據,生成數據代碼寫了一個小時,解決MySQL8的文件導入權限問題解決了兩個小時,導入數據花費了一個小時,我太難了~(;д;)。但是,一切不以實踐數據爲標準的理論都是**耍!流!氓!**o(´^`)o)。讓我們一邊講解MySQL的使用一邊看一下索引能爲我們的查詢帶來的性能提升吧。

索引使用的優勢

提高查詢效率,簡單來說就是查的再快更快!外面說的什麼提高表的速度、加速表連接、減少分組及排序時間、提高系統性能,說白了都是快,查得快!(順便我發現百度出來的索引使用優勢劣勢貌似就那麼一兩套,真就天下文章一大抄唄,抄我的也歡迎,煩請註明出處或者作者Solid_lele哈)

具體會有多快呢?

這是沒有索引的百萬級數據查找(這個算快的了,慢的四十秒,時間不是很穩定,因爲是從磁盤塊中讀取數據,原理參照我開頭提到的那篇文章)10.797s:

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

這是有索引的百萬級數據查找0.272s:

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

差了四十多倍,就相當於別人一年賺四十多萬,我一年賺一萬,這差距真的是太痛苦了。

索引使用的劣勢

凡事具有兩面性,有好就會有壞,拿時間換空間或者拿空間換時間這種操作屢見不鮮,索引就是拿空間換時間,雖然並不是那麼典型(因爲它核心並不是增大空間減少時間,而是通過維護類似目錄的結構減少IO的讀寫次數,最典型的空間換時間是計數排序)。壞處自然就出現了:

1、維護成本高

索引維護了一個類似於目錄的結構,你可以聯想新華字典的目錄,當你創建目錄的時候,如果沒有程序幫忙,你自己手寫目錄的話,需要一頁一頁的去翻去看確定那個字在哪兒,然後寫進目錄裏;萬一有個字被刪了或者加了一個字還要重新調整一遍目錄。對程序也是一樣,索引的創建和維護是需要消耗性能的,所以會降低數據庫修改時的性能。就以創建索引來說,創建百萬級別的varchar數據BTREE索引,數據內容長度爲20個漢字,消耗的時間爲61.234s:

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

再比較一下維護索引的代價,比如無索引百萬級別數據插入一條時間爲0.480s:

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

而有索引的百萬級別數據插入一條時間爲1.273s:

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

差距其實還挺大的(請忽略我亂打插入的三個字)。

2、所佔空間大

既然提到空間換時間,那麼空間的浪費是不可避免的,我做了下面的這個測試(測試數據庫MySQL8,數據庫運行環境Windows)。

首先創建了一個臨時表tmp_name,其中只有一列名爲c_name的字段,發現文件夾中存儲的ibd文件初始大小爲112k,插入百萬條數據(100萬條數據整哦,一個不多一個不少哦)後,大小爲40960k:

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

然後創建了一個BTREE索引,大小變爲了73728k:

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

大約多用了一倍的空間。但實際中不可能每個字段都加索引,生產中爲索引預留的空間大概佔數據大小的五分之一就夠了。在這個數據爲重效率至上的時代,磁盤的空間成本貌似還是比較划算的。

其實使用索引還有個劣勢,就是你需要花費時間來看我這篇文章o(////▽////)q

索引的分類

單值索引:就是一個索引只包含單個列,一個表可以有多個單列索引;唯一索引:就是索引列的值必須唯一,但允許有空值(主鍵索引就是唯一索引,但它不能爲null);複合索引:就是聯合索引,也就是一個索引包含多個列;

我看網上介紹索引的都說了唯一索引和複合索引,咋,單值索引就不算索引了?互相“借鑑”的時候好歹也自己思考一下好不啦。

索引的創建規則

索引既然有這麼明顯的優勢以及劣勢,我們自然要把它的優勢最大化,劣勢儘可能避免。所以索引最好能做到:

1、經常作爲查詢或者排序條件;

2、重複值儘可能少;

3、增刪改不會太多。

滿足上面三條規則的就可以創建索引了(不符合規則怎麼樣這種數據我實在是不想貼出來了,就直接說吧,這篇文章到現在算上造數據+各種實驗寫了十個小時了,還沒結束,又趕上LOL的無限火力,明明是週末卻只能羨慕別人在玩的我很是痛苦哇)。

所以總結一下適合創建的情況(就是以上面三個條件作爲參考的各種情況啦):

1、主鍵:主鍵是自動建立唯一索引的,不用咱操心;2、頻繁作爲查詢條件的字段:畢竟就是爲了查的快才創建索引的嘛;3、查詢中與其它表關聯的字段:這就是外鍵了,不僅關聯查詢用到了,重複值很少,很棒;4、如果有多個字段,儘量創建組合索引:當查詢優化器覺得分析兩個查詢索引太費勁了,還不如用一個的時候,它就給你用一個,所以只要遵循最佳左前綴原則,還是組合索引更靠譜;5、查詢中排序的字段:排序字段若通過索引去訪問將大大提高排序速度;6、查詢中統計或者分組字段:和上面情況一樣啦。

那什麼時候不適合創建呢:

1、表記錄太少而且不會變得很多:夭壽啦,就這麼幾百條數據建索引不如直接查一遍;

2、頻繁更新的字段不適合創建索引:維護索引很累的,查的沒這麼多但是總改總改,系統就像被經常需求變動的程序員,不跟你打起來就不錯了;

3、Where條件裏用不到的字段不創建索引:不要問爲什麼,問就是出門左轉是電梯;

索引的CRD沒有U

創建Create

#該語句添加一個主鍵,這意味着索引值必須是唯一的,且不能爲NULLALTER TABLE [table_name] ADD PRIMARY KEY ([column_list]);
#這條語句創建索引的值必須是唯一的(除了NULL外,NULL可能會出現多次)。
ALTER TABLE [table_name] ADD UNIQUE [index_name] ([column_list]);
#添加普通索引,索引值可出現多次。
ALTER TABLE [table_name] ADD INDEX [index_name] ([column_list]);
#該語句指定了索引爲 FULLTEXT ,用於全文索引。
ALTER TABLE [table_name] ADD FULLTEXT [index_name] ([column_list]);

查看Read

SHOW INDEX FROM [table_name];

刪除Drop

DROP INDEX [index_name] ON [table_name];

更新,不好意思沒有,想改就是刪了重加。

索引的分析(Explain)

終於走到這兒了,真刀真槍打一架吧,前面都是開胃小菜,現在纔是正餐,但估計你們都快喫飽了,我也做累了,就很尷尬。

Explain是什麼

話不多說,看官網API說明:

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

The EXPLAIN statement provides information about how MySQL executes statements. EXPLAIN works with SELECT, DELETE, INSERT, REPLACE, and UPDATE statements.

EXPLAIN returns a row of information for each table used in the SELECT statement. It lists the tables in the output in the order that MySQL would read them while processing the statement. MySQL resolves all joins using a nested-loop join method. This means that MySQL reads a row from the first table, and then finds a matching row in the second table, the third table, and so on. When all tables are processed, MySQL outputs the selected columns and backtracks through the table list until a table is found for which there are more matching rows. The next row is read from this table and the process continues with the next table.

EXPLAIN語句提供了關於MySQL怎樣執行語句的信息。EXPLAIN可以用來分析SELECT、DELETE、INSERT、REPLACE和UPDATE語句。EXPLAIN爲SELECT語句中使用的每個表返回一行信息。它按照MySQL在處理語句時讀取的順序,列出執行輸出中的表。MySQL使用嵌套循環連接方法解析所有連接。這意味着MySQL從第一個表中讀取一行,然後在第二個表、第三個表中找到匹配的行,以此類推。當處理完所有表後,MySQL將輸出所選的列,並通過表列表進行回溯,直到找到一個具有更多匹配行的表爲止。從該表讀取下一行,然後繼續處理下一個表。

說白了就是能讓你看錶的讀取順序、用索引情況、表之間的引用、優化器查詢的情況這些信息的執行情況分析。(不允許說每個漢字都認識湊到一起不知道啥意思了)

Explain的使用及分析

使用:就是Explain+你的sql語句:

EXPLAIN SELECT * FROM t_solid_test where c_name = 'Solid';

查詢出來長這個樣子:

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

這麼多信息都代表什麼?咱一個個來看。

id

select查詢的序列號,包含一組數字,表示查詢中執行select子句或操作表的順序。

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

id值越大,優先級越高,越先執行;id如果相同,從上往下順序執行。

select_type

查詢類型

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

SIMPLE:簡單的 select 查詢,查詢中不包含子查詢或者UNION;

PRIMARY:查詢中若包含任何複雜的子部分,最外層查詢則被標記爲;

UNION:若第二個SELECT出現在UNION之後,則被標記爲UNION; 若UNION包含在FROM子句的子查詢中,外層SELECT將被標記爲:DERIVED;

DEPENDENT UNION:一個UNION中的第二個或更高版本的SELECT語句 ,取決於外部查詢;

UNION RESULT:UNION的結果。

SUBQUERY:在SELECT或WHERE列表中包含了子查詢;

DEPENDENT SUBQUERY:在子查詢中的第一個SELECT,取決於外部查詢

DERIVED:在FROM列表中包含的子查詢被標記爲DERIVED(衍生) MySQL會遞歸執行這些子查詢, 把結果放在臨時表裏;

DEPENDENT DERIVED:派生表依賴於另一個表;

MATERIALIZED:物化子查詢;

UNCACHEABLE SUBQUERY:子查詢,其結果無法緩存,必須針對外部查詢的每一行重新進行評估;

UNCACHEABLE UNION:UNION 屬於不可緩存子查詢的中的第二個或更高版本的選擇(請參閱UNCACHEABLE SUBQUERY的參考資料 );

partitions

查詢分區的匹配記錄。如果未分區則爲NULL;

table

顯示這一行的數據是關於哪張表的;

type

訪問類型排列, 顯示查詢使用了何種類型:

system:表只有一行記錄(等於系統表),這是const類型的特列,平時不會出現,這個也可以忽略不計。

const:表示通過索引一次就找到了,const用於比較primary key或者unique索引。因爲只匹配一行數據,所以很快 如將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量。

eq_ref:唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描。

ref:非唯一性索引掃描,返回匹配某個單獨值的所有行. 本質上也是一種索引訪問,它返回所有匹配某個單獨值的行,然而, 它可能會找到多個符合條件的行,所以他應該屬於查找和掃描的混合體。

fulltext:使用FULLTEXT 索引執行聯接。

ref_or_null:這種連接類型類似於 ref,但是除了MySQL會額外搜索包含NULL值的行。

index_merge:此聯接類型指示使用索引合併優化。在這種情況下,key輸出行中的列包含所用索引的列表,並key_len包含所用索引 的最長鍵部分的列表。有關更多信息,請參見 第8.2.1.3節“索引合併優化”。

unique_subquery:此類型替換 以下形式的eq_ref某些 IN子查詢:value IN (SELECT primary_key FROM single_table WHERE some_expr),unique_subquery 只是一個索引查找函數,它完全替代了子查詢以提高效率。

index_subquery:此連接類型類似於 unique_subquery。它代替IN子查詢,但適用於以下形式的子查詢中的非唯一索引:value IN (SELECT key_column FROM single_table WHERE some_expr)。

range:只檢索給定範圍的行,使用一個索引來選擇行。key 列顯示使用了哪個索引 一般就是在你的where語句中出現了between、<、>、in等的查詢 這種範圍掃描索引掃描比全表掃描要好,因爲它只需要開始於索引的某一點,而結束於另一點,不用掃描全部索引。

index:Full Index Scan,index與ALL區別爲index類型只遍歷索引樹。這通常比ALL快,因爲索引文件通常比數據文件小(也就是說雖然all和Index都是讀全表,但index是從索引中讀取的,而all是從硬盤中讀的)。

all:Full Table Scan,將遍歷全表以找到匹配的行。備註:一般來說,得保證查詢至少達到range級別,最好能達到ref。

possible_keys

顯示可能應用在這張表中的索引,一個或多個。 查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定被查詢實際使用。

key

實際使用的索引。如果爲NULL,則沒有使用索引。查詢中若使用了覆蓋索引,則該索引和查詢的select字段重疊。

key_len

表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度。在不損失精確性的情況下,長度越短越好。key_len顯示的值爲索引字段的最大可能長度,並非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的。

ref

顯示索引的哪一列被使用了,如果可能的話,是一個常數。哪些列或常量被用於查找索引列上的值。

rows

根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數。

Extra

包含不適合在其他列中顯示但十分重要的額外信息:

Using filesort:說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中無法利用索引完成的排序操作稱爲“文件排序”。

Using temporary:使用了臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序order by 和分組查詢 group by。

USING index:表示相應的select操作中使用了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯! 如果同時出現using where,表明索引被用來執行索引鍵值的查找; 如果沒有同時出現using where,表明索引用來讀取數據而非執行查找動作。

Using where:表明使用了where過濾。

using join buffer:使用了連接緩存。

impossible where:where子句的值總是false,不能用來獲取任何元素。

select tables optimized away:在沒有GROUPBY子句的情況下,基於索引優化MIN/MAX操作或者對於MyISAM存儲引擎優化COUNT(*)操作,不必等到執行階段再進行計算, 查詢執行計劃生成的階段即完成優化。

distinct:優化distinct操作,在找到第一匹配的元組後即停止找同樣值的動作

索引使用案例

最理想的情況

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

最佳左前綴原則

後臺創建的索引是name_sex_age的聯合索引,聯合索引中,從左往右匹配,如果最開始匹配不到,則索引失效。

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

儘量不用函數操作索引

在索引列上做任何操作(計算、函數、(自動or手動)類型轉換),會導致索引失效而轉向全表掃描。

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

索引中範圍條件右邊的列不會被使用

下面這個聯合索引,當聯合索引中間的值查詢條件爲範圍查詢時,右側的索引不會被用到。

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

少用select *

儘量使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減少select *。

MySQL索引怎麼用?究竟能有多快?看完這篇你就懂了

 

索引失效的幾個情況

不等於(!= 或者<>)、is null、is not null 、or、like以通配符開頭(’%abc…’)、字符串不加單引號(類型轉換)

這就不貼圖了,沒啥可貼的,用了type就是ALL;

索引使用的總結建議

最後,給一個索引使用的總結吧:

對於單鍵索引,儘量選擇針對當前query過濾性更好的索引。在選擇組合索引的時候,當前Query中過濾性最好的字段在索引字段順序中,位置越靠前越好。在選擇組合索引的時候,儘量選擇可以能夠包含當前query中的where字句中更多字段的索引。儘可能通過分析統計信息和調整query的寫法來達到選擇合適索引的目的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章