MySQL 索引優(yōu)化的指導(dǎo)性建議

MySQL 提供 MyISAM 愧薛、 InnoDB 戒洼、 memory(heap) 等多種存儲引擎抖棘。每種存儲引擎對于索引的支持以及實現(xiàn)都不盡相同箭券,
本文主要討論 InnoDB 引擎相關(guān)的索引應(yīng)用净捅。

為何使用索引

索引用于快速找出在某個列中有一特定值的行。在查詢時如果沒有應(yīng)用索引辩块,MySQL 將不得不掃描表以找出符合條件的數(shù)據(jù)項蛔六,我們知道,IO操作是非常耗時的废亭。

建立索引的幾個原則

  • 盡量使用唯一索引国章,對于有唯一值的列索引效果最好,對于像性別只有很少的值的列索引效果就不明顯豆村。

  • 索引長度盡量短液兽,這樣做有幾個好處,首先短的索引可以節(jié)省索引空間掌动,也會使查找的速度得到提升四啰。對于較短的鍵值宁玫,索引高速緩存中的塊能容納更多的鍵值,MySQL也可以在內(nèi)存中容納更多的值柑晒。

  • 太長的列撬统,可以選擇只建立部分索引

  • 更新非常頻繁的數(shù)據(jù)不適宜建索引

  • 利用最左前綴原則,比如建立了一個聯(lián)合索引(a,b,c)敦迄,那么其實我們可利用的索引就有(a), (a,b), (a,b,c)

  • 不要過多創(chuàng)建索引恋追,首先過多的索引會占用更多的空間,而且每次增罚屋、刪苦囱、改操作都會重建索引,而且過多索引也會增加之后的優(yōu)化復(fù)雜度

  • 盡量擴展索引脾猛,比如現(xiàn)有索引(a)撕彤,現(xiàn)在我又要對(a,b)進(jìn)行索引,不需要再建一個索引(a,b)

  • 請注意猛拴,一次查詢是不能應(yīng)用多個索引的

  • <羹铅,<=,=愉昆,>职员,>=,BETWEEN跛溉,IN 可用到索引焊切,<>,not in 芳室,!= 則不行

  • like "xxxx%" 是可以用到索引的专肪,like "%xxxx" 則不行(like "%xxx%" 同理)

  • NULL會使索引的效果大打折扣

淺談聯(lián)合索引

首先因為 InnoDB 的數(shù)據(jù)文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有)堪侯,如果沒有顯式指定嚎尤,則MySQL系統(tǒng)會自動選擇一個可以唯一標(biāo)識數(shù)據(jù)記錄的列作為主鍵,如果不存在這種列伍宦,則MySQL自動為InnoDB表生成一個隱含字段作為主鍵芽死。主鍵對于 InnoDB 的索引結(jié)構(gòu)是十分重要的。

InnoDB 引擎的索引是使用 B+樹 實現(xiàn)索引結(jié)構(gòu)的雹拄,當(dāng)我們建立一個聯(lián)合索引(a, b, c)時收奔,B+樹將按照從左至右來建立搜索樹掌呜,然后檢索時B+樹將先比較 a 然后再其基礎(chǔ)上比較 b 和 c 滓玖。不難看出如果我們的搜索條件中只有 a 和 c ,將不能使用完整的(a, b, c)索引质蕉,如果我們的搜索條件中沒有 a 那么這條查詢將不會用上索引势篡,這其實就是最左前綴特性翩肌。

** 接下來我們來看下聯(lián)合索引應(yīng)用時的幾種情況: **

desc users;

+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| rname | varchar(50) | NO   |     | NULL    |                |
| rdesc | varchar(50) | NO   |     | NULL    |                |
| age   | int(11)     | NO   | MUL | NULL    |                |
| card  | int(5)      | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+

show index from users;

+-------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name      | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| users |          0 | PRIMARY       |            1 | id          | A         |          15 |     NULL | NULL   |      | BTREE      |         |               |
| users |          1 | age_name_desc |            1 | age         | A         |          15 |     NULL | NULL   |      | BTREE      |         |               |
| users |          1 | age_name_desc |            2 | rname       | A         |          15 |       10 | NULL   |      | BTREE      |         |               |
| users |          1 | age_name_desc |            3 | rdesc       | A         |          15 |       15 | NULL   |      | BTREE      |         |               |
+-------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
  • 全列匹配
explain select * from users where age = 11 and rname = "asd" and rdesc = "asd";

+----+-------------+-------+------+---------------+---------------+---------+-------------------+------+-------------+
| id | select_type | table | type | possible_keys | key           | key_len | ref               | rows | Extra       |
+----+-------------+-------+------+---------------+---------------+---------+-------------------+------+-------------+
|  1 | SIMPLE      | users | ref  | age_name_desc | age_name_desc | 83      | const,const,const |    1 | Using where |
+----+-------------+-------+------+---------------+---------------+---------+-------------------+------+-------------+

以上 explain 的結(jié)果可以看出,當(dāng)對索引中所有列進(jìn)行精確匹配的時候禁悠,可以用到完整索引念祭。

explain select * from users where  rname = "asd" and age = 11 and rdesc = "asd";

+----+-------------+-------+------+---------------+---------------+---------+-------------------+------+-------------+
| id | select_type | table | type | possible_keys | key           | key_len | ref               | rows | Extra       |
+----+-------------+-------+------+---------------+---------------+---------+-------------------+------+-------------+
|  1 | SIMPLE      | users | ref  | age_name_desc | age_name_desc | 83      | const,const,const |    1 | Using where |
+----+-------------+-------+------+---------------+---------------+---------+-------------------+------+-------------+

按照B+樹的結(jié)構(gòu)聯(lián)合索引本是對順序十分敏感的,但是從以上結(jié)果可看出調(diào)整順序并沒有影響到索引的選用碍侦,這是因為MySQL的查詢優(yōu)化器會自動調(diào)整where子句的條件順序以使用適合的索引粱坤。

  • 部分匹配(左前綴)
explain select * from users where age = 11;

+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key           | key_len | ref   | rows | Extra |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------+
|  1 | SIMPLE      | users | ref  | age_name_desc | age_name_desc | 4       | const |    1 | NULL  |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------+

從上述結(jié)果可看出當(dāng)精確匹配最左前綴的列時,是可以用到索引的瓷产,但是 key_len = 4站玄,只用到了第一列前綴索引。

explain select * from users where age = 11 and rdesc = "asd";

+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key           | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
|  1 | SIMPLE      | users | ref  | age_name_desc | age_name_desc | 4       | const |    1 | Using where |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+

上述查詢?nèi)笔Я藃name列濒旦,可以看出當(dāng)缺失中間列時將不能使用完整的聯(lián)合索引株旷。查詢只用到了最左部分索引,而rdesc由于沒有rname無法和左前綴銜接尔邓。

explain select * from users where rname = "asd";

+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL |   15 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

上述查詢由于查詢條件不是最左前綴晾剖,將不能使用聯(lián)合索引age_name_desc,建立聯(lián)合索引時順序是很重要的梯嗽,必須在建索引前考慮清楚齿尽。

  • 非精確查詢
explain select * from users where age = 11 and rname like 'sad%';

+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
| id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra                              |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
|  1 | SIMPLE      | users | range | age_name_desc | age_name_desc | 36      | NULL |    1 | Using index condition; Using where |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+

explain select * from users where age = 11 and rname like 'sad%' and rdesc = 'asd';

+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
| id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra                              |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
|  1 | SIMPLE      | users | range | age_name_desc | age_name_desc | 83      | NULL |    1 | Using index condition; Using where |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+

可以看出like查詢當(dāng)通配符'%'不出現(xiàn)在開頭時,是可以應(yīng)用到索引的灯节。

explain select * from users where age = 11 and rname like '%sad%';

+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key           | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
|  1 | SIMPLE      | users | ref  | age_name_desc | age_name_desc | 4       | const |    1 | Using where |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+

通配符'%'出現(xiàn)在開頭則不行

接下來我們看看范圍查詢

explain select * from users where age = 61 and rname < 'fasd';

+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
| id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra                              |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
|  1 | SIMPLE      | users | range | age_name_desc | age_name_desc | 36      | NULL |    1 | Using index condition; Using where |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
  • 函數(shù)和表達(dá)式
explain select * from users where (age + 1) = 33;

+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL |   15 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

當(dāng)查詢條件中出現(xiàn)函數(shù)或表達(dá)式時雕什,將不能應(yīng)用索引。寫查詢語句時要盡量避免表達(dá)式和函數(shù)的出現(xiàn)显晶。

指導(dǎo)性建議

建議在選擇性高的列上建立索引贷岸,所謂索引的選擇性,是指不重復(fù)的索引值與表記錄數(shù)的比值:

Index Selectivity = Cardinality / Count

顯然選擇性的取值范圍為(0, 1]磷雇,選擇性越高的索引價值越大偿警。
比例越大我們掃描的記錄數(shù)越少,唯一鍵的區(qū)分度是1唯笙,而一些狀態(tài)螟蒸、性別字段可能在大數(shù)據(jù)面前區(qū)分度就趨近于0

select count(distinct(rname))/count(*) as selectivity from users;

+-------------+
| selectivity |
+-------------+
|      0.9333 |
+-------------+

有一種與索引選擇性有關(guān)的索引優(yōu)化策略叫做前綴索引,就是用列的前綴代替整個列作為索引key崩掘,當(dāng)前綴長度合適時七嫌,可以做到既使得前綴索引的選擇性接近全列索引,同時因為索引key變短而減少了索引文件的大小和維護(hù)開銷苞慢。

select count(distinct(left(rname, 3)))/count(*) as selectivity from users;

+-------------+
| selectivity |
+-------------+
|      0.7333 |
+-------------+

select count(distinct(left(rname, 5)))/count(*) as selectivity from users;

+-------------+
| selectivity |
+-------------+
|      0.9333 |
+-------------+

可以看到诵原,當(dāng)把前綴取到5時selectivity值就和完整的selectivity值一樣了,這樣可以大幅度減小索引所占用的空間,而且相應(yīng)的查詢速度也會有一定提升绍赛。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蔓纠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子吗蚌,更是在濱河造成了極大的恐慌腿倚,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚯妇,死亡現(xiàn)場離奇詭異敷燎,居然都是意外死亡,警方通過查閱死者的電腦和手機箩言,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門懈叹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人分扎,你說我怎么就攤上這事澄成。” “怎么了畏吓?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵墨状,是天一觀的道長。 經(jīng)常有香客問我菲饼,道長肾砂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任宏悦,我火速辦了婚禮镐确,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饼煞。我一直安慰自己源葫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布砖瞧。 她就那樣靜靜地躺著息堂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪块促。 梳的紋絲不亂的頭發(fā)上荣堰,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音竭翠,去河邊找鬼振坚。 笑死,一個胖子當(dāng)著我的面吹牛斋扰,可吹牛的內(nèi)容都是我干的渡八。 我是一名探鬼主播啃洋,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼呀狼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起损离,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤哥艇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后僻澎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體貌踏,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年窟勃,在試婚紗的時候發(fā)現(xiàn)自己被綠了祖乳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秉氧,死狀恐怖眷昆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情汁咏,我是刑警寧澤亚斋,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站攘滩,受9級特大地震影響帅刊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜漂问,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一赖瞒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚤假,春花似錦栏饮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芒划,卻和暖如春冬竟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背民逼。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工泵殴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拼苍。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓笑诅,卻偏偏與公主長得像调缨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子吆你,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容