創(chuàng)建高性能的索引 1

索引基礎(chǔ)

SELECT first_name FROM sakila.actor WHERE actor_id = 5;

如果 actor_id 列上有索引,這 MySQL 將使用該索引找到 actor_id 為5的列,也就是說 MySQL 會(huì)在索引上按值進(jìn)行查找,然后返回所有包含改值得數(shù)據(jù)行.

索引可以包含一個(gè)或者多個(gè)列的值.如果索引包含多個(gè)列,列的順序也很重要,因?yàn)?MySQL 只能高效的使用索引的最左前綴列.創(chuàng)建一個(gè)包含兩個(gè)列的索引和創(chuàng)建兩個(gè)只包含一列的索引是大不相同的

如果使用的是 ORM, 是否還需要關(guān)心索引?
仍然需要

索引的類型

索引有很多類型, 現(xiàn)在MySQL中,索引是在存儲(chǔ)引擎層而不是服務(wù)器層實(shí)現(xiàn)的.所以不同的存儲(chǔ)引擎有不同的索引類型

B-Tree 索引

存儲(chǔ)引擎以不同的方式使用 B-Tree 索引,性能也各有不同,各有優(yōu)劣.

MyISAM 使用前綴壓縮技術(shù)使得索引更小,但是 InnoDB 則按照原數(shù)據(jù)格式進(jìn)行存儲(chǔ).在 InnoDB 中使用 B+Tree.MyISAM索引通過數(shù)據(jù)的物理位置引用被索引的行,而InnoDB 則根據(jù)主鍵引用被索引的行.

B-Tree意味著所有的值都是按照順序存儲(chǔ)的,并且每一個(gè)葉子頁到根的距離相同.下圖表示了B-Tree索引的抽象表示,大致反映了 InnoDB 索引是如何工作的.


建立是在 B-Tree 結(jié)構(gòu)(從技術(shù)上來說是 B+Tree)上d 額索引

B-Tree索引能加快訪問數(shù)據(jù)的速度,因?yàn)榇鎯?chǔ)引擎不再需要進(jìn)行全表掃描來獲取需要的數(shù)據(jù),取而代之是從索引的根節(jié)點(diǎn)進(jìn)行搜索.關(guān)于 B+Tree 的數(shù)據(jù)結(jié)構(gòu)介紹,可以參見我的數(shù)據(jù)結(jié)構(gòu)相關(guān)的文章.

請(qǐng)注意,索引對(duì)多個(gè)值進(jìn)行排序的依據(jù)是 CREATE TABLE 語句中定義索引時(shí)列的順序.

可以使用 B-Tree 索引的查詢類型. B-Tree 索引適合全鍵值,鍵值范圍或鍵前綴查找.其中鍵前綴查找只適合于根據(jù)最左前綴的查找. B-Tree 對(duì)如下類型的查詢有效:

全值匹配
匹配最左前列
匹配列前綴
匹配范圍值
精確匹配某一列并范圍匹配另外一列
只訪問索引的查詢

因?yàn)樗饕龝械墓?jié)點(diǎn)是有序的,所以除了按值查找之外,索引還可以用于查詢中的 ORDER BY 操作.一般來說如果 B-Tree 可以按照某種方式查找到值,那么也可以按照這種方式用于排序.

下面是一些關(guān)于 B-Tree 索引的限制:

  • 如果不是按照索引的最左列開始查找,則無法使用索引.
  • 不能跳過索引中的列
  • 如果查詢中有某個(gè)列的范圍查詢,則其右邊所有的列都無法使用索引優(yōu)化查找,比如使用 LIKE. 如果范圍查詢劣質(zhì)的數(shù)量有限,那么可以通過使用多個(gè)等于條件來替代范圍條件.

綜上所述,索引列的順序是多的重要.索引再優(yōu)化性能的時(shí)候,需要根據(jù)不用的也無需求來設(shè)計(jì)索引列的順序

Cardinality

  • 什么是Cardinality? 通過 SHOW INDEX 來查看數(shù)據(jù)表的 Cardinality項(xiàng),表示索引中以為只記錄數(shù)量的預(yù)估值.當(dāng) Cardinality/n_rows_in_table 應(yīng)盡可能的等于1,否則就要考慮是否需要索引當(dāng)前列.
  • Cardinality的統(tǒng)計(jì) 采用采樣的方法: 1.表中1/16的數(shù)據(jù)發(fā)生了變化;2.stat_modified_counter > 2000000000

B+ 樹索引的使用

在不同應(yīng)用中使用
  • OLTP 應(yīng)用中查詢操作中只從數(shù)據(jù)庫取一小部分?jǐn)?shù)據(jù),這是建立 B+樹索引很有意義
  • OLAP 應(yīng)用中需要訪問大量的數(shù)據(jù),如果 OLAP 中的復(fù)雜查詢涉及多張表之間的聯(lián)結(jié)操作,這是添加索引有意義,如果聯(lián)結(jié)操作使用 HASH Join, 那么索引可能又變得沒有必要.
聯(lián)合索引

??聯(lián)合索引需要注意的事項(xiàng)在上面 B-Tree 都說介紹的很詳細(xì)了.

覆蓋索引
優(yōu)化器選擇不使用索引的情況

??在某些情況下發(fā)現(xiàn)優(yōu)化器并沒有使用索引去查找數(shù)據(jù),而是進(jìn)行了全表掃描.這種情況多發(fā)生于范圍查找,Join 等操作.例如:

mysql> explain SELECT * FROM tb_cinema WHERE cinema_id > 100 and cinema_id < 120;
+----+-------------+-----------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
| id | select_type | table     | partitions | type  | possible_keys | key       | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-----------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | tb_cinema | NULL       | range | cinema_id     | cinema_id | 4       | NULL |    5 |   100.00 | Using index condition |
+----+-------------+-----------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain SELECT * FROM tb_cinema WHERE cinema_id > 100 and cinema_id < 46349;
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_cinema | NULL       | ALL  | cinema_id     | NULL | NULL    | NULL | 9014 |    50.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

通過上面的例子可以看出,如果要求訪問的數(shù)據(jù)量很小那么優(yōu)化器會(huì)選擇使用輔助索引,但是當(dāng)訪問的數(shù)據(jù)量占整個(gè)表中數(shù)據(jù)的很大一部分時(shí)(超過20%),優(yōu)化器會(huì)選擇通過聚集索引來查數(shù)據(jù).如果用戶使用的是固態(tài)硬盤同時(shí)確保使用輔助索引能帶來更好的性能,那么使用關(guān)鍵字 FORCE INDEX 來強(qiáng)制使用某個(gè)索引:

mysql> explain SELECT * FROM tb_cinema FORCE INDEX(cinema_id) WHERE cinema_id > 100 and cinema_id < 46349;
+----+-------------+-----------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
| id | select_type | table     | partitions | type  | possible_keys | key       | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-----------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | tb_cinema | NULL       | range | cinema_id     | cinema_id | 4       | NULL | 4507 |   100.00 | Using index condition |
+----+-------------+-----------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.01 sec)
INDEX HINT (索引提示)

一下兩種情況使用到索引提示:

  • MySQL 錯(cuò)誤的選擇了某個(gè)索引,這種情況非常少.
  • 某個(gè) SQL 語句可以選擇的索引非常多,這時(shí)優(yōu)化器選擇執(zhí)計(jì)劃時(shí)間的開銷可能會(huì)大于 SQL 語句本身.這時(shí)需要通過INDEX HINT來強(qiáng)制使用優(yōu)化器不進(jìn)行各個(gè)執(zhí)行路徑的成本分析,直接執(zhí)行選擇指定的索引來完成查詢

哈希索引

MySQL 中只有 Memory 引擎顯式支持哈希索引.這里不多介紹

空間索引

MyISAM 表支持空間索引,可以用作地理數(shù)據(jù)存儲(chǔ).和 B-Tree 不一樣,這類索引無序前綴查詢.MySQL 對(duì) GIS 支持并不完善, PostgreSQL支持的比較好.

全文索引

全文索引是一種特殊類型的索引,它查找的事文本中的關(guān)鍵詞,而不是直接比較索引中的值.全文索引和其他幾類索引的匹配方式完全不一樣.全文索引更類似于搜索引擎做的事情,而不是簡單的 WHERE 條件匹配.以后的文章會(huì)有詳細(xì)介紹更多關(guān)于全文索引的細(xì)節(jié).

索引的優(yōu)點(diǎn)及使用Tips

索引可以讓服務(wù)器更快速的定位到表的指定位置.但這不是索引的唯一作用.

最常見的 B-Tree 索引,按照順序存儲(chǔ)數(shù)據(jù),所以 MySQL 可以用來做 ORDER BY 和 GROUP BY 操作.因?yàn)閿?shù)據(jù)是有序的,所以 B-Tree 也就會(huì)將相關(guān)的列值都存儲(chǔ)到一起.以下總結(jié)索引的三個(gè)優(yōu)點(diǎn):

  • 索引大大減少了服務(wù)器需要掃描的數(shù)據(jù)量
  • 索引可以幫助服務(wù)器避免排序如臨時(shí)表.
  • 索引可以將隨機(jī) I/O 變?yōu)轫樞?I/O.

同時(shí),對(duì)于非常小的表,大部分情況下簡單的全表掃描更高效.對(duì)于中到大型的表,索引就非常的有效.但是對(duì)于特別大的表,建立和使用索引的代價(jià)將隨之增長.這種情況下需要一種技術(shù)可以直接區(qū)分出查詢需要的一組數(shù)據(jù),而不是一條一條的記錄的匹配.例如可以使用分區(qū)技術(shù).如果表的數(shù)量特別多,可以建立一個(gè)元數(shù)據(jù)信息表,用來查詢需要用到的某些特性.例如執(zhí)行那些需要聚合多個(gè)表的數(shù)據(jù)的查詢,則需要記錄"哪個(gè)用戶的信息存儲(chǔ)在哪個(gè)表中"的元數(shù)據(jù),這樣在查詢時(shí)就可以直接忽略那些不包含指定和用戶信息.對(duì)于大型系統(tǒng)(TB 級(jí)別的數(shù)據(jù)),只是一個(gè)常用的技巧,使用塊級(jí)別元數(shù)據(jù)技術(shù)來替代索引.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末号俐,一起剝皮案震驚了整個(gè)濱河市贷腕,隨后出現(xiàn)的幾起案子翎猛,更是在濱河造成了極大的恐慌玉转,老刑警劉巖也殖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烁竭,死亡現(xiàn)場離奇詭異粪滤,居然都是意外死亡官套,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門删咱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屑埋,“玉大人,你說我怎么就攤上這事痰滋∪副耍” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵即寡,是天一觀的道長。 經(jīng)常有香客問我袜刷,道長聪富,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任著蟹,我火速辦了婚禮墩蔓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萧豆。我一直安慰自己奸披,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布涮雷。 她就那樣靜靜地躺著阵面,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洪鸭。 梳的紋絲不亂的頭發(fā)上样刷,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音览爵,去河邊找鬼置鼻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蜓竹,可吹牛的內(nèi)容都是我干的箕母。 我是一名探鬼主播储藐,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嘶是!你這毒婦竟也來了钙勃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤俊啼,失蹤者是張志新(化名)和其女友劉穎肺缕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體授帕,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡同木,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了跛十。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彤路。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖芥映,靈堂內(nèi)的尸體忽然破棺而出洲尊,到底是詐尸還是另有隱情,我是刑警寧澤奈偏,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布坞嘀,位于F島的核電站,受9級(jí)特大地震影響惊来,放射性物質(zhì)發(fā)生泄漏丽涩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一裁蚁、第九天 我趴在偏房一處隱蔽的房頂上張望矢渊。 院中可真熱鬧,春花似錦枉证、人聲如沸矮男。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毡鉴。三九已至,卻和暖如春舞萄,著一層夾襖步出監(jiān)牢的瞬間眨补,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工倒脓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撑螺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓崎弃,卻偏偏與公主長得像甘晤,于是被迫代替她去往敵國和親含潘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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