日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

MySQL 为什么用索引,为什么是 B+树,怎么用索引

發(fā)布時(shí)間:2023/12/20 数据库 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL 为什么用索引,为什么是 B+树,怎么用索引 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

MySQL 索引

A database index is a data structure that improves the speed of operations in a table. Indexes can be created using one or more columns, providing the basis for both rapid random lookups and efficient ordering of access to records.

為什么需要索引

根據(jù)上面索引的定義,可以知道索引其實(shí)是一種數(shù)據(jù)結(jié)構(gòu),主要用于提高表中的查詢效率,除此之外,索引還是數(shù)據(jù)庫(kù)隨機(jī)高速讀取和對(duì)記錄進(jìn)行有效排序的基礎(chǔ)。

不使用索引情況下數(shù)據(jù)的讀取

除了像 Redis 這樣的內(nèi)存型數(shù)據(jù)庫(kù)外,大部分的關(guān)系型數(shù)據(jù)庫(kù)如 MySQL 等的數(shù)據(jù)都是直接存儲(chǔ)在磁盤上的,而對(duì)于從磁盤查找數(shù)據(jù)來說,需要經(jīng)歷尋道尋址數(shù)據(jù)傳輸三個(gè)階段。

  • 尋道:驅(qū)動(dòng)器驅(qū)動(dòng)磁頭前后移動(dòng)到對(duì)應(yīng)的磁道,一般為 5 ~ 14 ms
  • 尋址:磁盤旋轉(zhuǎn)到指定扇區(qū)的過程,尋址時(shí)間與磁盤轉(zhuǎn)速有關(guān),對(duì)于一個(gè) 7200 轉(zhuǎn)的磁盤來說,意味著一分鐘轉(zhuǎn) 7200 圈,每秒可以轉(zhuǎn) 120 圈,在尋址時(shí),最好情況下磁頭正好在正確扇區(qū)不需要再次尋址,最差情況下需要轉(zhuǎn)一圈才能到正確扇區(qū),所以尋址的平均時(shí)間為 1/120/2=4.17ms1/120/2 = 4.17ms1/120/2=4.17ms
  • 數(shù)據(jù)傳輸:數(shù)據(jù)傳輸階段的耗時(shí)主要包括兩部分,一是磁頭從磁盤讀取到數(shù)據(jù)并存儲(chǔ)到磁盤緩存所需要的時(shí)間,二是從磁盤緩存中讀取數(shù)據(jù)到對(duì)應(yīng)控制器所需的時(shí)間;數(shù)據(jù)傳輸耗時(shí)主要與硬件性能有關(guān),但一般為零點(diǎn)幾毫秒。

所以直接從磁盤讀取數(shù)據(jù)的 IO 耗時(shí)一般在 10ms 左右,為了避免頻繁的磁盤 IO,所以操作系統(tǒng)在讀取數(shù)據(jù)時(shí)會(huì)以頁(yè)為單位,一次讀取目標(biāo)數(shù)據(jù)以及和目標(biāo)數(shù)據(jù)相鄰的一頁(yè)大小(4K或8K)的數(shù)據(jù)并放在緩存中,這樣下次再讀取相鄰的數(shù)據(jù)時(shí)就可以直接從緩存中返回了。

MySQL 一次會(huì)讀四頁(yè)也就是 16 K

在不使用索引的情況下,如果要查詢最后一條數(shù)據(jù),就需要從頭遍歷到尾,
這種情況下,數(shù)據(jù)庫(kù)需要讀取所有的片才能得到目標(biāo)數(shù)據(jù),大量時(shí)間會(huì)浪費(fèi)在磁盤 IO 上,為此,我們需要一種數(shù)據(jù)結(jié)構(gòu)去記錄數(shù)據(jù)項(xiàng)和磁盤中頁(yè)的關(guān)系,這樣在查詢某條記錄時(shí)就可以直接定位到某一頁(yè),這樣只需要進(jìn)行一次磁盤IO便可以得到目標(biāo)數(shù)據(jù),可以大大優(yōu)化查詢效率,這種數(shù)據(jù)結(jié)構(gòu)便是索引。

為什么是 B+ 樹

要實(shí)現(xiàn)上面的功能,首先可以采用 Hash Table 的方式,將索引鍵 Hash 之后存儲(chǔ)哈希值和鍵對(duì)應(yīng)的行指針,這樣一來,在使用哈希索引查詢的時(shí)候就可以直接計(jì)算出要查詢記錄的哈希值,然后查詢此哈希值對(duì)應(yīng)的行指針,由于每一行所需要的存儲(chǔ)空間是固定的,所以得到行指針就相當(dāng)于定位到了記錄對(duì)應(yīng)的頁(yè),這時(shí)每次查詢只需要進(jìn)行一次磁盤 IO, 可以大大優(yōu)化查詢效率,但哈希索引存在一些問題:

  • 哈希沖突: 只要使用 Hash Table 的數(shù)據(jù)結(jié)構(gòu),哈希沖突就是不可避免的,MySQL 中解決沖突的方式是拉鏈法,即一旦發(fā)生沖突就把新的記錄以鏈表的方式鏈接到原來的記錄之后,這樣每次查詢都需要先遍歷這個(gè)鏈表得到一個(gè)行指針,再根據(jù)行指針查詢記錄,得到記錄后再與要查詢的記錄作比較,如果得到的不是要查詢的記錄,要回去取鏈表中的下一個(gè)行指針,再去查詢比較,直到得到期望的數(shù)據(jù),因此使用哈希索引后的磁盤IO次數(shù)取決于沖突的發(fā)生率,在存在大量沖突時(shí),哈希索引的查詢效率會(huì)急速下降。
  • 哈希索引只支持等值查詢:由于哈希索引是根據(jù)哈希鍵計(jì)算出哈希值,所以它只能在進(jìn)行等值查詢(如 IN, =, <=>)時(shí)才能起到優(yōu)化效率的效果,在進(jìn)行非等值操作(如 !=, >, <, <>)時(shí)起不到任何作用。
  • 組合索引:在使用組合索引時(shí),哈希索引的做法是將所有索引鍵合并后再做哈希,這就導(dǎo)致對(duì)多個(gè)字段做組合索引后,再查詢其中某一個(gè)字段時(shí)無法利用索引。
  • 無法根據(jù)索引進(jìn)行有效排序,哈希之后的的值已經(jīng)丟失了原來的索引鍵的大小信息,所以無法根據(jù)索引進(jìn)行高效排序

除了使用 Hash Table, 另一個(gè)思路是使用排序樹,以排序樹的結(jié)構(gòu)組織頁(yè)后,可以將原來查詢 O(n)O(n)O(n)的復(fù)雜度降低到 lg?n\lg nlgno(n)o(n)o(n)的復(fù)雜度就意味著每次查詢需要進(jìn)行 nnn 次磁盤IO,使用排序樹后雖然不能像哈希表一樣達(dá)到 O(1)O(1)O(1) 的復(fù)雜度,但相比不使用索引可以大大減少磁盤 IO 的次數(shù) 。

MySQL 中默認(rèn)使用 B+ 樹構(gòu)建索引,之所以使用 B+ 樹而不是 B 樹或二叉排序樹的原因在于:

  • 要選取的樹結(jié)構(gòu)必須是穩(wěn)定的,如果采用二叉排序樹,在插入有序序列后,二叉樹就會(huì)退化為鏈表,起不到好的優(yōu)化效果
  • 根據(jù)排序樹查詢其實(shí)是在進(jìn)行樹的深度遍歷,而每遍歷一個(gè)樹節(jié)點(diǎn)都是一次磁盤IO,所以具體的IO次數(shù)取決于樹的高度,這就要求樹要盡可能矮,也就要求能一個(gè)根節(jié)點(diǎn)能持有多個(gè)子節(jié)點(diǎn)。
  • B+ 樹就滿足上面兩點(diǎn)要求,首先 B+ 樹是一棵多路平衡二叉樹,其次由于磁盤IO以固定大小的頁(yè)為單位,所以每次進(jìn)行磁盤IO能夠查詢出的數(shù)據(jù)量是有限制的,這同樣意味著樹的一個(gè)父節(jié)點(diǎn)能夠持有的子節(jié)點(diǎn)數(shù)量是有限的,而 B+ 樹的數(shù)據(jù)只存儲(chǔ)在葉子節(jié)點(diǎn),中間節(jié)點(diǎn)只存儲(chǔ)指針,這使得每個(gè)中間節(jié)點(diǎn)能持有更多的子節(jié)點(diǎn),相比 B 樹,B+ 樹的高度更低,且每次查詢都必須遍歷到葉子節(jié)點(diǎn),使得 B+ 樹的查詢穩(wěn)定性更高。

    雖然上面說 B+ 樹的葉子節(jié)點(diǎn)存儲(chǔ)數(shù)據(jù),但具體到 MySQL 對(duì)索引的實(shí)現(xiàn)上,葉子節(jié)點(diǎn)存儲(chǔ)的依然不是真正的數(shù)據(jù),存儲(chǔ)的只是指向真實(shí)數(shù)據(jù)的指針,當(dāng)然聚簇索引除外,聚簇索引存儲(chǔ)數(shù)據(jù)的順序和索引順序是一致的,一張表也只能建立一個(gè)聚簇索引,一般用于主鍵索引。

    索引類型

    MySQL 索引根據(jù)用途不同可以分為以下幾種類型:

  • 普通索引(INDEX)

  • 唯一索引(UNIQUE INDEX)

  • 主鍵索引(PRIMARY KEY)

  • 組合索引(UNION INDEX)

  • 全文索引(FULLTEXT ):這是針對(duì)大量文本數(shù)據(jù)的一種特殊所索引,其組織形式也與一般索引不盡相同,主要用于查找文本中的關(guān)鍵字,只能建立在 char、varchar,text 列上, 需要注意的是,直到 MySQL 5.6 InnoDB 引擎才支持了全文索引,在這之前只有 MyISAM 支持, 同時(shí),全文索引一般配合 match against 使用,而不是 where, 關(guān)于全文索引的用法,可以參考知乎這篇文章

    值得一提的是,在數(shù)據(jù)量較大時(shí)候,現(xiàn)將數(shù)據(jù)放入一個(gè)沒有全局索引的表中,然后再用CREATE index創(chuàng)建fulltext索引,要比先為一張表建立fulltext然后再將數(shù)據(jù)寫入的速度快很多。

  • 創(chuàng)建索引

    創(chuàng)建索引有三種方式;

  • 創(chuàng)建表時(shí)直接定義

  • 使用 ALTER 語(yǔ)句修改表結(jié)構(gòu)創(chuàng)建

  • 直接使用 CREATE TABLE 命令創(chuàng)建:

    CREATE TABLE table_name[col_name data type] [unique|fulltext][index|key][index_name](col_name[length])[asc|desc]
    • unique|fulltext 為可選參數(shù),分別表示唯一索引、全文索引

    • index 和 key 為同義詞,兩者作用相同,用來指定創(chuàng)建索引

    • col_name 為需要?jiǎng)?chuàng)建索引的字段列,該列必須從數(shù)據(jù)表中該定義的多個(gè)列中選擇

    • index_name 指定索引的名稱,為可選參數(shù),如果不指定,默認(rèn)col_name為索引值

    • length 為可選參數(shù),表示索引的長(zhǎng)度,只有字符串類型的字段才能指定索引長(zhǎng)度

    • asc 或 desc 指定升序或降序的索引值存儲(chǔ)

  • 刪除索引:

  • 直接刪除:

    DROP index_name ON table_name;
  • 通過修改表結(jié)構(gòu)刪除

    ALTER TABLE table_name DROP index_name;
  • 索引建立的原則

    天下沒有免費(fèi)的午餐,索引也并不是萬(wàn)能的,它帶來高查詢效率的同時(shí)也會(huì)帶來一些問題:

  • 占用更多的磁盤空間(一般不考慮)
  • 導(dǎo)致較低的寫效率:由于索引需要維持一個(gè)龐大的樹結(jié)構(gòu),加上這是一棵排序樹,這就會(huì)導(dǎo)致某些插入和修改操作會(huì)造成樹的重建,因此索引帶來高查詢效率的同時(shí)會(huì)導(dǎo)致較低的寫效率。
  • 所以對(duì)一些不應(yīng)該建立索引的列建立索引后可能導(dǎo)致更差的性能,在考量某一列是否應(yīng)該建立索引時(shí)需要參考一個(gè)重要的法則:最左前綴法則,不滿足該法則可能導(dǎo)致索引失效進(jìn)而退化成全表掃描。

    最左前綴法則

    最左前綴法則是建立聯(lián)合索引時(shí)最重要的法則。

    mysql會(huì)一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配.

    如以下的表結(jié)構(gòu):

    CREATE TABLE `left` (`id` int(11) NOT NULL AUTO_INCREMENT,`a` varchar(255) NOT NULL,`b` varchar(255) NOT NULL,`c` varchar(255) NOT NULL,`d` varchar(255) NOT NULL,PRIMARY KEY (`id`),KEY `index1` (`a`,`b`,`c`) ) ENGINE=InnoDB AUTO_INCREMENT=86139 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

    我們建立了 a, b, c 的組合索引后:

  • 在進(jìn)行等值查詢?nèi)?#61; 或 IN 時(shí), 可以不考慮順序,SQL 查詢優(yōu)化器會(huì)自動(dòng)調(diào)整語(yǔ)句順序,如執(zhí)行下面兩條語(yǔ)句的效果是一樣的(根據(jù)索引長(zhǎng)度我們可以推斷出對(duì)哪幾個(gè)列使用了索引):

  • 可以查詢建立了聚合索引的某幾列,DBS會(huì)根據(jù)建立索引時(shí)的順序從左開始匹配能夠使用索引的列,如執(zhí)行a = “” AND b = “” 時(shí)會(huì)對(duì) a 和 b 使用索引,而在執(zhí)行 a = “” AND c = “" 時(shí)則只會(huì)對(duì) a 使用索引,而如果只執(zhí)行 b = “” 時(shí),由于第一個(gè)索引 a 就無法匹配到,所以不會(huì)使用索引

  • 最左匹配原則在遇到范圍查詢:>、<、between、like 時(shí)會(huì)停止匹配,如執(zhí)行 a = “” AND b > 10 AND c = “” 時(shí)只會(huì)對(duì) a 使用索引。

    • 關(guān)于 like: like 相比于其他幾個(gè)要稍微復(fù)雜一點(diǎn),并不是一旦遇見like就會(huì)停止匹配,而是通配符如果出現(xiàn)在首位才會(huì)停止匹配,如下面的情況:

  • 對(duì)索引列進(jìn)行運(yùn)算操作會(huì)導(dǎo)致索引失效,原因與 like 的通配符一樣,還有需要注意一點(diǎn),如果索引字段是字符類型,查詢時(shí)不加引號(hào)也會(huì)導(dǎo)致索引失效,原因在于MySQL會(huì)自動(dòng)為我們的查詢語(yǔ)句轉(zhuǎn)化成字符,這就相當(dāng)于引入了運(yùn)算操作:

    SELECT * FROM `left` WHERE a = "" AND b = 1 -- 將 1 轉(zhuǎn)化為 '1' 的過程引入了運(yùn)算操作導(dǎo)致索引失效
  • 如果 OR 之后的字段沒有使用索引,那么整個(gè)索引都將失效。

  • NOT IN 可能導(dǎo)致的索引失效

  • 為什么要最左匹配

    首先要明確的是最左匹配原則適用于聯(lián)合索引,對(duì)于普通索引,不存在匹配的問題,而之所以要嚴(yán)格進(jìn)行最左匹配,也是由聯(lián)合索引的數(shù)據(jù)結(jié)構(gòu)形式?jīng)Q定的:

    我們知道 MySQL 默認(rèn)情況下使用 B+ 樹組織索引的數(shù)據(jù)結(jié)構(gòu),對(duì)于像上文中的聯(lián)合索引,它的結(jié)構(gòu)是這樣的:

    相比普通索引的葉子節(jié)點(diǎn),聯(lián)合索引的葉子節(jié)點(diǎn)存儲(chǔ)所有關(guān)鍵字的數(shù)據(jù),比如建立了a, b, c的索引,那么如上圖,每個(gè)葉子節(jié)點(diǎn)都會(huì)存儲(chǔ)a, b, c 三個(gè)關(guān)鍵字的數(shù)據(jù)信息,并且會(huì)按照建立索引時(shí)的順序排序,但中間節(jié)點(diǎn)只會(huì)存儲(chǔ)第一個(gè)關(guān)鍵字的位置指針,當(dāng)我們執(zhí)行類似

    SELECT * FROM `table` WHERE a = "1" AND b = "2" AND c = "4"

    時(shí),數(shù)據(jù)庫(kù)會(huì)根據(jù)第一個(gè)關(guān)鍵字 a 的值 1 定位到某個(gè)葉子(圖中左邊的葉子節(jié)點(diǎn)),然后從所有葉子節(jié)點(diǎn)的數(shù)據(jù)里檢索出符合第一條規(guī)則a = "1" 的數(shù)據(jù)(圖中前六行),然后再?gòu)倪@些數(shù)據(jù)里檢索出符合第二條規(guī)則的數(shù)據(jù)(圖中2, 3, 4)行,依次類推直到找到或確認(rèn)找不到期望數(shù)據(jù)為止。

    而之所以遵循最左匹配原則,也是因?yàn)槿~子節(jié)點(diǎn)的排序方式是按照索引建立時(shí)的順序排序的,也就是 b 只有在 a 相等的情況下才是有序的(如圖中第二列整體并不是有序的,但只看 a = 1 前提下的 b 就是有序的了),所以如果跳過 a 去查詢 b, 因?yàn)闊o法保證 b 的有序性,只能進(jìn)行全表掃描。

    like 之所以遇到以通配符開頭的情況才停止匹配也是由葉子節(jié)點(diǎn)的這種數(shù)據(jù)排序方式?jīng)Q定的,因?yàn)?like 字句如果不以通配符開頭那他開頭的部分是可以利用到排序信息的,如執(zhí)行:

    SELECT * FROM `left` WHERE a = "1" AND b LIKE "2%"

    雖然 b 的檢索不是等值檢索,但我們?nèi)稳豢梢愿鶕?jù) like 子句開頭的 “2” 快速定位到 2 ~ 4 行,但如果以通配符開頭,顯然就定位不到了。

    索引建立原則

  • 對(duì)經(jīng)常需要修改的數(shù)據(jù)不要建立索引,一般數(shù)據(jù)的讀寫比為 10:1, 如果低于此,索引可能造成寫數(shù)據(jù)效率低下

  • 對(duì)于重復(fù)讀高的數(shù)據(jù)不建議建立索引,如性別,區(qū)分度公式為:
    count(distinctcol)count(?)\frac{count(distinct col)}{count(*)} count(?)count(distinctcol)?

    最好的區(qū)分度為 111 ,即所有數(shù)據(jù)不重復(fù),一般要求區(qū)分度高于 0.1

  • 不建議對(duì)不經(jīng)常查詢的列或 “大數(shù)據(jù)” 建立索引,如 TXT, 二進(jìn)制信息等。

  • 建議給主鍵和外鍵建立索引,一來主鍵是唯一的,通過索引檢索可以大大提高定位速度,其次為外鍵建立索引也可以提高表之間連接的速度

  • 對(duì)于經(jīng)常出現(xiàn)在 WHERE 子句中的或經(jīng)常按范圍查詢的列,建議建立索引,由于 MySQL 中使用指針連接了葉子節(jié)點(diǎn),所以對(duì)于按范圍查詢的列,建立索引后可以進(jìn)一步降低磁盤 IO。

  • 索引列不能參與計(jì)算,保持列“干凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡(jiǎn)單,b+樹中存的都是數(shù)據(jù)表中的字段值,但進(jìn)行檢索時(shí),需要把所有元素都應(yīng)用函數(shù)才能比較,顯然成本太大。所以語(yǔ)句應(yīng)該寫成create_time = unix_timestamp(’2014-05-29’)。

  • 盡量的擴(kuò)展索引,不要新建索引。比如表中已經(jīng)有a的索引,現(xiàn)在要加(a,b)的索引,那么只需要修改原來的索引即可。

  • 索引跳躍式掃描(INDEX SKIP SCAN)

    加入我們建立了 a, b, c 順序的組合索引,但 a 的區(qū)分度不高,然后執(zhí)行了 WHERE b = "" AND ... 就會(huì)出現(xiàn) INDEX SKIP SCAN 的情況, 也就是說 SQL 查詢優(yōu)化器跳過了 a 對(duì)后面的列使用了索引,如下面這種情況:

    上圖中 songs 表結(jié)構(gòu)如下:

    +--------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+--------------+------+-----+---------+-------+ | id | bigint(25) | NO | PRI | NULL | | | name | varchar(255) | NO | | NULL | | | link | varchar(200) | NO | | NULL | | | singer | int(11) | YES | | NULL | | +--------+--------------+------+-----+---------+-------+

    并且為該表建立了 singer, name, link 順序的組合索引, 但 singer 的區(qū)分度約為 0.04, 很低:

    mysql> select COUNT(DISTINCT left(singer,110))/COUNT(*) AS singer, COUNT(DISTINCT left(name,255))/COUNT(*) AS name, COUNT(DISTINCT left(link,200))/COUNT(*) AS link FROM songs ; +--------+--------+--------+ | singer | name | link | +--------+--------+--------+ | 0.0390 | 0.7715 | 1.0000 | +--------+--------+--------+ 1 row in set (25.82 sec)

    在這種情況下,由于 singer 的區(qū)分度很低,所以全表掃描查詢 singer 字段的代價(jià)并不是很高,同樣對(duì)于 singer 來說,使用索引的效果并不明顯,但相比之下,后面的 name 和 link 字段的區(qū)分度很高,使用索引的效果會(huì)非常明顯,這時(shí)如果由于 “無關(guān)緊要” 的 singer 導(dǎo)致后面真正需要索引的 name 和 link 無法使用索引顯然得不償失,因此在 MySQL 8.0 之后加入了 ISS 機(jī)制,它允許組合索引在左邊的列唯一值較少的情況下跳過左邊列對(duì)右邊列使用索引。

    成本優(yōu)先

    索引的出現(xiàn)本就是為了降低查詢成本的,但若在某些情況下使用索引反而增加了查詢成本,那就不應(yīng)該使用索引,MySQL 在執(zhí)行查詢前會(huì)預(yù)估查詢成本,然后根據(jù)成本決定是否使用索引或使用哪個(gè)索引,不使用索引時(shí)的查詢成本包括兩部分:

  • IO 成本:指的是把數(shù)據(jù)從磁盤讀到內(nèi)存的成本,前面說到磁盤IO以頁(yè)為單位,如果每次磁盤IO的成本記為 1, 那么讀取一條數(shù)據(jù)的 IO成本就是:
    Cost(IO)=Data_lengthpage_sizeCost(IO) = \frac{Data\_length}{page\_size} Cost(IO)=page_sizeData_length?

  • CPU 成本:是指將數(shù)據(jù)讀入內(nèi)存后,還要檢測(cè)數(shù)據(jù)是否滿足條件和排序等 CPU 操作的成本,一般默認(rèn)情況下每行的 CPU 成本約為 0.2

  • 而如果表中有索引,在執(zhí)行查詢前,數(shù)據(jù)庫(kù)引擎會(huì)估算使用索引所需要的成本,具體估算方法參考:xx , 估算出的值可以通過 optimizer_trace 工具查看,如果索引的成本高于全表掃描的成本,那就會(huì)放棄索引。

    如執(zhí)行 :

    SET optimizer_trace="enabled=on"; SELECT * FROM `left` WHERE a > "1"; SELECT * FROM information_schema.OPTIMIZER_TRACE; SET optimizer_trace="enabled=off";

    后得到使用 index1 索引的成本為 54705

    "analyzing_range_alternatives": {"range_scan_alternatives": [{"index": "index1","ranges": ["1 < a"],"index_dives_for_eq_ranges": true,"rowid_ordered": false,"using_mrr": false,"index_only": false,"rows": 49977,"cost": 54705,"chosen": false,"cause": "cost"}],"analyzing_roworder_intersect": {"usable": false,"cause": "too_few_roworder_scans"} }

    但全表掃描成本為:


    0.2?Rows+Data_lengthpage_size=0.2?99954+473497616?1024=19990.80.2 * Rows + \frac{Data\_length}{page\_size} = 0.2 * 99954 + \frac{4734976}{16 * 1024} = 19990.8 0.2?Rows+page_sizeData_length?=0.2?99954+16?10244734976?=19990.8
    顯然全表掃描的效率要高于使用索引的效率。

    需要注意的是數(shù)據(jù)庫(kù)引擎只是只是在估算成本,這個(gè)值不一定準(zhǔn)確,上面的例子從最左前綴的角度也不應(yīng)該使用索引,只是為了說明并不是在任何時(shí)候數(shù)據(jù)庫(kù)引擎都會(huì)去使用索引的在涉及到低區(qū)分度,Null 值等的時(shí)候,引擎會(huì)選取一個(gè)相對(duì)最優(yōu)的方案。

    索引的使用建議

    一. 避免索引失效

  • 謹(jǐn)慎選擇組合索引的建立順序
  • 涉及非等值操作查詢時(shí),謹(jǐn)慎安排查詢語(yǔ)句的順序,避免范圍查詢導(dǎo)致索引失效
  • 不要在索引字段上執(zhí)行計(jì)算操作
  • 匹配字符串時(shí)不要依賴 MySQL 的類型轉(zhuǎn)換
  • 謹(jǐn)慎使用 OR
  • 謹(jǐn)慎選擇 IN, NOT IN, EXISTS
  • 二. 使用索引覆蓋

    覆蓋索引指的是索引字段覆蓋了需要查詢的所有字段,這時(shí)根據(jù)索引便可以拿到所有數(shù)據(jù)而不需要回表查詢,反之,如果使用類似 SELECT * 或 索引字段未覆蓋期望的所有字段時(shí),未被覆蓋的字段就需要回表查詢,這便又增加了磁盤 IO 的次數(shù),如果發(fā)生了回表查詢, EXPLAIN 的描述(Extra)字段會(huì)顯示 Using index condition 這時(shí)我們應(yīng)該考慮是否需要優(yōu)化。

    三. 有時(shí)全表掃描更快

    索引不一定能 100% 提高查詢效率,使用不當(dāng)反而會(huì)使性能下降

    四. 盡量使用復(fù)合索引

    在每次查詢時(shí),數(shù)據(jù)庫(kù)只會(huì)選擇一個(gè)最優(yōu)的索引使用,所以使用復(fù)合索引往往優(yōu)于使用多個(gè)單列索引。

    總結(jié)

  • 什么是索引:

    • 索引是一種數(shù)據(jù)結(jié)構(gòu),用來提高在數(shù)據(jù)表中的數(shù)據(jù)查詢效率,同時(shí)也是隨機(jī)讀和有效排序的基礎(chǔ)。
  • 為什么使用索引:

    • 根本原因在于磁盤速度與內(nèi)存速度差距甚大,所以我們希望能使用盡可能少的磁盤 IO 次數(shù)去拿到想要的數(shù)據(jù),因此引入了索引,索引通過哈希表或 B+ 樹的方式存儲(chǔ)了索引值和數(shù)據(jù)塊的對(duì)應(yīng)關(guān)系,使得能夠在較低的時(shí)間復(fù)雜度內(nèi)拿到數(shù)據(jù)。
  • InnoDB 中為什么選擇 B+ 樹組織索引:

    • 實(shí)現(xiàn)索引的數(shù)據(jù)結(jié)構(gòu)必須能在較低的時(shí)間復(fù)雜度內(nèi)找到索引鍵對(duì)應(yīng)的數(shù)據(jù),除了哈希表外,可以選擇排序樹,同時(shí)為了減少磁盤 IO 次數(shù),要求這棵樹要盡可能低,要實(shí)現(xiàn)自平衡,不能在極端情況下退化為鏈表,再者,由于操作系統(tǒng)以頁(yè)為單位進(jìn)行磁盤 IO,這就意味這不能為了降低樹高度無限增加一個(gè)樹節(jié)點(diǎn)的子節(jié)點(diǎn),所以為了保證一個(gè)中間節(jié)點(diǎn)持有更多子節(jié)點(diǎn)而選擇 B+ 樹而不是 B 樹,另外,B+ 樹所有數(shù)據(jù)存儲(chǔ)在葉子節(jié)點(diǎn),這樣每次查詢的時(shí)間復(fù)雜度都是一致的,可以獲得更高的穩(wěn)定性。
  • 聚簇索引和非聚簇索引的區(qū)別:

    • 聚簇索引在一張表中只能有一個(gè),一般是主鍵索引,聚簇索引的葉子節(jié)點(diǎn)存儲(chǔ)的是真實(shí)地?cái)?shù)據(jù)。
    • 非聚簇索引可以建立多個(gè),其葉子節(jié)點(diǎn)存儲(chǔ)地并不是真實(shí)地?cái)?shù)據(jù),而是主鍵值,根據(jù)非聚簇索引只能拿到該行記錄地主鍵值,要拿到真實(shí)地?cái)?shù)據(jù)還需要根據(jù)聚簇索引去查詢
  • 在什么情況下使用索引:

    • 讀操作比例大大高于寫操作比例時(shí)。
    • 數(shù)據(jù)區(qū)分度高。
    • 主鍵和外鍵建議使用。
    • 經(jīng)常出現(xiàn)在 WHERE 子句中的列。
  • 如何高效地使用索引:

    • 建立索引時(shí)盡量使用組合索引。
    • 不要對(duì)大量數(shù)據(jù)建立索引。
    • 建立組合索引時(shí)認(rèn)真考慮先后順序。
    • 使用索引時(shí)嚴(yán)格遵循最左前綴原則,避免索引失效。
    • 盡量使用索引覆蓋,避免 SELECT *
  • 參考

    【tutorialspoint】mysql-indexes

    【美團(tuán)技術(shù)文章】MySQL索引原理及慢查詢優(yōu)化

    【知乎】平衡二叉樹、B樹、B+樹、B*樹 理解其中一種你就都明白了

    【CSDN】數(shù)據(jù)庫(kù)中的索引技術(shù)——哈希索引

    【360doc】數(shù)據(jù)庫(kù)建立索引的原則

    【博客園】sql 索引類型

    索引跳躍式掃描(INDEX SKIP SCAN)

    【騰訊云】 MySQL中IS NULL、IS NOT NULL、!=不能用索引?胡扯!

    總結(jié)

    以上是生活随笔為你收集整理的MySQL 为什么用索引,为什么是 B+树,怎么用索引的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 久久久精品人妻无码专区 | 日本精品少妇 | 久久有精品 | av污 | 国产主播在线看 | 日日爽夜夜操 | 亚洲欧美综合色 | 亚洲婷婷在线视频 | 国产四区 | 777奇米色 | 久久久精品国产sm调教 | 欧美成欧美va | 性感美女高潮 | 日韩精品一区二区三区不卡在线 | 日韩美女国产精品 | 香港日本韩国三级网站 | 亚洲一区二区三区综合 | 一起操在线 | 色网综合| 免费精品视频 | 亚洲欧美日韩一区二区三区四区 | 久久久精品视频一区 | 亚洲精品鲁一鲁一区二区三区 | 国产乱淫av片 | 亚洲乱码一区二区三区在线观看 | 光棍影院一区二区 | 天天干天天色天天 | 黑人精品xxx一区一二区 | 青青草成人av | 神马午夜电影一区二区三区在线观看 | 欧美日韩第一区 | 久操视频免费在线观看 | 亚洲综合大片69999 | 视频黄色免费 | 美女少妇一区二区 | 91精品人妻一区二区三区四区 | 免费看欧美大片 | 美女被草视频 | 毛片在线观看视频 | 午夜毛片在线 | 欧美一二区 | 国产在线黄| 免费国产一级 | 超碰男人的天堂 | 日韩中文一区二区三区 | av在线小说 | 精品无码在线视频 | 日韩午夜激情视频 | 日韩白浆 | 亚洲区一区 | 成人tiktok黄短视频 | 亚洲第一成肉网 | 国产精品女人久久久 | 60分钟| 国产精品午夜无码专区 | 亚洲黄色免费在线观看 | 天天干天天色天天 | 中文字幕欧美日韩 | av免费网站在线观看 | 久久av片 | 日本少妇xxxx软件 | 欧美成人性生活片 | 国产精品美女久久久久图片 | 天天操操夜夜操操 | 日韩经典在线 | 丰满av| 日韩欧美一二三四区 | 久久久精品人妻一区二区三区 | 中文字幕免费高清 | 亚洲天堂手机在线 | 国产精品高清无码在线观看 | 国产页 | 99热这里只有精品在线 | 国产福利在线观看视频 | 亚洲a黄| 亚洲精品黄 | 日本一区二区三区免费看 | 波多野结衣人妻 | 亚洲欧美精品aaaaaa片 | 欧美激情在线播放 | 亚洲yy| av网在线播放 | 中国极品少妇xxxxx | 久色亚洲| 草莓视频在线观看18 | 国产农村妇女毛片精品久久 | 亚洲精品尤物 | 精品人妻少妇嫩草av无码专区 | 最新中文字幕久久 | 亚洲一区二区三区蜜桃 | av毛片在线免费观看 | 黑丝美女一区二区 | 欧州一区二区三区 | 中文在线免费 | 亚洲视频精品一区 | 亚洲午夜久久久久久久久 | 精品久久久久久久免费人妻 | 免费毛片在线 | 69久久精品无码一区二区 |