圖文并茂地講解Mysql索引(index)
前言
本篇文章相對來說篇幅較長,不是一會半會能看完的,建議您收藏起來慢慢看,關(guān)于索引的相關(guān)知識基本上都記錄全了,通過這一篇文章足以讓您的Mysql知識更上一層樓!
1. 索引概述
1.1 什么是索引?
索引(index)是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)(有序)。在數(shù)據(jù)之外,數(shù)據(jù)庫系統(tǒng)還維護著滿足特定查找算法的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)以某種方式引用(指向)數(shù)據(jù), 這樣就可以在這些數(shù)據(jù)結(jié)構(gòu)上實現(xiàn)高級查找算法,這種數(shù)據(jù)結(jié)構(gòu)就是索引。
想要學(xué)習(xí)好索引,那么就一定要掌握mysql的數(shù)據(jù)結(jié)構(gòu),其實在一提到數(shù)據(jù)結(jié)構(gòu),對于基礎(chǔ)較差的來說,有時候是非常頭疼的,不過在這里大家完全不用擔心,接下來也會重點講解數(shù)據(jù)結(jié)構(gòu),盡量會以白話文的形式敘述每一個數(shù)據(jù)結(jié)構(gòu)?。?!

1.2 使用索引和不使用索引的區(qū)別
在這里我們主要演示不使用索引和使用索引的區(qū)別到底有多大。
表結(jié)構(gòu)及其數(shù)據(jù)如下:

假如我們要執(zhí)行的SQL語句為 : select * from user where age = 45;
(1)無索引情況
在無索引情況下,就需要從第一行開始掃描,一直掃描到最后一行,我們稱之為 全表掃描,性能很低。可能有的人該說了,明明在id為7的數(shù)據(jù)已經(jīng)找到age為45的數(shù)據(jù),為什么還是全表掃描呢?
因為對于mysql當中他并不知道后面是否還存在age為45的數(shù)據(jù),所以他會不落下任何一條數(shù)據(jù)!

(2)有索引情況
如果我們針對于這張表的age字段建立了索引,假設(shè)索引結(jié)構(gòu)就是二叉樹,那么也就意味著,會對age這個字段建立一個二叉樹的索引結(jié)構(gòu)。而這個二叉樹當中每個節(jié)點存儲了真正數(shù)據(jù)的位置,我們只要在樹當中找到了對應(yīng)的age就意味著找到了真正的數(shù)據(jù)!
如下圖:當查找age為45的時候,這時候會從根節(jié)點開始判斷,根節(jié)點為36,比36大所以開始走右邊的節(jié)點,光這一下子直接排除掉樹的左邊數(shù)據(jù),然后又進行判斷比48小,這時候走左邊節(jié)點,然后就找到了,只需要掃描三次就可以找到數(shù)據(jù)了,極大的提高的查詢的效率。

不管是二叉樹還是B+樹,一定都是有順序的,他都是在新增數(shù)據(jù)的時候,根據(jù)數(shù)據(jù)的大小進行了排序然后分叉。也正因為如此,所以提高了查詢速度!
備注: 這里我們只是假設(shè)索引的結(jié)構(gòu)是二叉樹,介紹一下索引的大概原理,只是一個示意圖,并不是索引的真實結(jié)構(gòu),索引的真實結(jié)構(gòu),后面會詳細介紹。
1.3 索引的特點

降低數(shù)據(jù)庫的IO,什么是IO?
IO就是所謂的流,流又分為了讀和寫,當我們想要從文件當中找數(shù)據(jù)就需要讀,當需要修改文件的時候就需要寫,Mysql最終存儲的數(shù)據(jù)都是在磁盤文件當中,那么我們想要找一條數(shù)據(jù),怎么辦呢?
先想想我們現(xiàn)實當中想要在一個文件找有沒有哪個數(shù)據(jù)是怎么找的呢,直接打開文件,然后全局搜索,假如文件比較大的話,搜索也會有點卡頓。mysql他跟我們可不一樣,我們那屬于是人家windows系統(tǒng)給我們提供了這種便捷,我們可以直接打開文件,然后進行搜索。
mysql假如是全表掃描,首先需要從數(shù)據(jù)文件當中 將這張表的數(shù)據(jù)給全部讀取到內(nèi)存,然后再進行判斷哪個數(shù)據(jù)是符合條件的。其中這也考驗到了我們電腦的讀的能力,當然越高配置的電腦讀取速度越快。
假如加了索引,我們只需要將索引給讀取出來,因為索引他指向了數(shù)據(jù)在文件上的地址。所以只需要找到對應(yīng)數(shù)據(jù)的索引,然后通過索引獲取到數(shù)據(jù)的位置,再從數(shù)據(jù)文件當中將這條數(shù)據(jù)給讀取出來即可,也因此降低了IO成本。
如果數(shù)據(jù)集都讀取到內(nèi)存,假如電腦內(nèi)存只有16G,而這張表有200G,一旦全表掃描,電腦豈不是直接掛掉了?
實際上,服務(wù)端并不需要保存一個完整的結(jié)果集。取數(shù)據(jù)和發(fā)數(shù)據(jù)的流程是這樣的:
- 獲取一行,寫到 net_buffer 中。這塊內(nèi)存的大小是由參數(shù) net_buffer_length 定義的,默認是 16k。
- 重復(fù)獲取行,直到 net_buffer 寫滿,調(diào)用網(wǎng)絡(luò)接口發(fā)出去。
- 如果發(fā)送成功,就清空 net_buffer,然后繼續(xù)取下一行,并寫入 net_buffer。
- 如果發(fā)送函數(shù)返回 EAGAIN 或 WSAEWOULDBLOCK,就表示本地網(wǎng)絡(luò)棧(socket send buffer)寫滿了,進入等待。直到網(wǎng)絡(luò)棧重新可寫,再繼續(xù)發(fā)送。
所以我們在使用過程,基本上不可能會因為mysql查詢數(shù)據(jù)而導(dǎo)致服務(wù)器內(nèi)存爆滿,mysql主要是占用我們服務(wù)器的IO。
2. 索引結(jié)構(gòu)
2.1 概述
MySQL的索引是在存儲引擎層實現(xiàn)的,不同的存儲引擎有不同的索引結(jié)構(gòu),主要包含以下幾種:

上述是MySQL中所支持的所有的索引結(jié)構(gòu),接下來,我們再來看看不同的存儲引擎對于索引結(jié)構(gòu)的支持情況。

注意: 實際開發(fā)當中會重點使用B+Tree,所以本篇我們也會重點講解B+Tree的存儲結(jié)構(gòu)!我們平常所說的索引,如果沒有特別指明,都是指B+Tree結(jié)構(gòu)組織的索引。
2.2 二叉樹
假如說MySQL的索引結(jié)構(gòu)采用二叉樹的數(shù)據(jù)結(jié)構(gòu),比較理想的結(jié)構(gòu)如下:

如果主鍵是順序插入的,則會形成一個單向鏈表,結(jié)構(gòu)如下:
所謂的順序就是恰好每次插入的都比上個節(jié)點小,或者大,這樣就會形成一個鏈表

所以,如果選擇二叉樹作為索引結(jié)構(gòu),會存在以下缺點:
- 順序插入時,會形成一個鏈表,查詢性能大大降低。
- 大數(shù)據(jù)量情況下,層級較深,檢索速度慢。
此時大家可能會想到,我們可以選擇紅黑樹,紅黑樹是一顆自平衡二叉樹,那這樣即使是順序插入數(shù)據(jù),最終形成的數(shù)據(jù)結(jié)構(gòu)也是一顆平衡的二叉樹,結(jié)構(gòu)如下:

但是,即使如此,由于紅黑樹也是一顆二叉樹,所以也會存在一個缺點:
- 大數(shù)據(jù)量情況下,層級較深,檢索速度慢。
所以,在MySQL的索引結(jié)構(gòu)中,并沒有選擇二叉樹或者紅黑樹,而選擇的是B+Tree,那么什么是B+Tree呢?在詳解B+Tree之前,先來介紹一個B-Tree。
2.3 B-Tree
在說B+Tree之前,我們先了解一下B-Tree,B-Tree又被稱之為B樹,而B+Tree是B-Tree的變種,B樹是一種多叉路平衡查找樹,相對于二叉樹,B樹每個節(jié)點可以有多個分支,即多叉。
以一顆最大度數(shù)(max-degree)為5(5階)的b-tree為例,那這個B樹每個節(jié)點最多存儲4個key,5個指針,指針永遠比key最多多1個:

知識小貼士: 樹的度數(shù)指的是一個節(jié)點的子節(jié)點個數(shù)。
我們可以通過一個數(shù)據(jù)結(jié)構(gòu)可視化的網(wǎng)站來簡單演示一下。https://www.cs.usfca.edu/~galles/visualization/BTree.html

插入一組數(shù)據(jù): 100 65 169 368 900 556 780 35 215 1200 234 888 158 90 1000 88 120 268 250 。然后觀察一些數(shù)據(jù)插入過程中,節(jié)點的變化情況。
插入數(shù)據(jù)過程一:由于設(shè)置的為五階,五階最多存儲4個key,5個指針,一旦節(jié)點存儲的key數(shù)量到達5,就會裂變。

插入數(shù)據(jù)過程二:直接進行了裂變,中間元素向上分裂

插入數(shù)據(jù)過程三:

插入數(shù)據(jù)過程四:這時候會發(fā)現(xiàn)556放到了右邊節(jié)點的中間位置,因為B-TREE是有序的

如下是最終結(jié)果,后面的我就不再演示了,強烈建議大家自己去網(wǎng)站插入看一下,這樣可以更好的熟悉是數(shù)據(jù)結(jié)構(gòu)!

B-Tree特點:
- 5階的B樹,每一個節(jié)點最多存儲4個key,對應(yīng)5個指針。
- 一旦節(jié)點存儲的key數(shù)量到達5,就會裂變,中間元素向上分裂。
- 在B樹中,非葉子節(jié)點和葉子節(jié)點都會存放數(shù)據(jù)。
2.4 B+Tree
B+Tree是B-Tree的變種,我們以一顆最大度數(shù)(max-degree)為4(4階)的b+tree為例,來看一下其結(jié)構(gòu)示意圖:

我們可以看到,兩部分:
- 綠色框框起來的部分,是索引部分,僅僅起到索引數(shù)據(jù)的作用,不存儲數(shù)據(jù)。
- 紅色框框起來的部分,是數(shù)據(jù)存儲部分,在其葉子節(jié)點中要存儲具體的數(shù)據(jù)。
我們可以通過一個數(shù)據(jù)結(jié)構(gòu)可視化的網(wǎng)站來簡單演示一下。
https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html
插入一組數(shù)據(jù): 100 65 169 368 900 556 780 35 215 1200 234 888 158 90 1000 88 120 268 250 。然后觀察一些數(shù)據(jù)插入過程中,節(jié)點的變化情況。
如下是最終插入的結(jié)果展示:

最終我們看到,B+Tree 與 B-Tree相比,主要有以下三點區(qū)別:
所有的數(shù)據(jù)都會出現(xiàn)在葉子節(jié)點。葉子節(jié)點形成一個單向鏈表。非葉子節(jié)點僅僅起到索引數(shù)據(jù)作用,具體的數(shù)據(jù)都是在葉子節(jié)點存放的。
上述我們所看到的結(jié)構(gòu)是標準的B+Tree的數(shù)據(jù)結(jié)構(gòu),接下來,我們再來看看MySQL中優(yōu)化之后的B+Tree。
- 所有的數(shù)據(jù)都會出現(xiàn)在葉子節(jié)點。
- 葉子節(jié)點形成一個單向鏈表。
- 非葉子節(jié)點僅僅起到索引數(shù)據(jù)作用,具體的數(shù)據(jù)都是在葉子節(jié)點存放的。

mysql當中一頁代表了B+TREE數(shù)據(jù)結(jié)構(gòu)當中的一個葉子節(jié)點,并且一頁固定大小為16kb。
總結(jié):mysql的B+Tree數(shù)據(jù)結(jié)構(gòu),就是在原來的B+Tree結(jié)構(gòu)基礎(chǔ)上,將葉子節(jié)點的單向鏈表改為了雙向鏈表
2.5 Hash
MySQL中除了支持B+Tree索引,還支持一種索引類型—Hash索引。
(1) 結(jié)構(gòu)
哈希索引就是采用一定的hash算法,將鍵值換算成新的hash值,映射到對應(yīng)的槽位上,然后存儲在hash表中。
以下圖為例:將name設(shè)置為hash索引,假如想要找對應(yīng)的name數(shù)據(jù),首先會對name進行hash計算得出一個下標值,通過下標值來獲取到對應(yīng)的數(shù)據(jù)。

如果兩個(或多個)鍵值,映射到一個相同的槽位上,他們就產(chǎn)生了hash沖突(也稱為hash碰撞),可以通過鏈表來解決。

(2) 特點
- Hash索引只能用于對等比較(=,in),不支持范圍查詢(between,>,< ,…)
- 無法利用索引完成排序操作
- 查詢效率高,通常(不存在hash沖突的情況)只需要一次檢索就可以了,效率通常要高于B+tree索 引
(3) 存儲引擎支持
在MySQL中,支持hash索引的是Memory存儲引擎。 而InnoDB中具有自適應(yīng)hash功能,hash索引是InnoDB存儲引擎根據(jù)B+Tree索引在指定條件下自動構(gòu)建的。
思考題: 為什么InnoDB存儲引擎選擇使用B+tree索引結(jié)構(gòu)?
- 相對于二叉樹,層級更少,搜索效率高;
- 對于B-tree,無論是葉子節(jié)點還是非葉子節(jié)點,都會保存數(shù)據(jù),這樣導(dǎo)致一頁中存儲的鍵值減少,指針跟著減少,要同樣保存大量數(shù)據(jù),只能增加樹的高度,導(dǎo)致性能降低;
- 相對Hash索引,B+tree支持范圍匹配及排序操作;
3.索引分類
3.1 索引分類
在MySQL數(shù)據(jù)庫,將索引的具體類型主要分為以下幾類:主鍵索引、唯一索引、常規(guī)索引、全文索引。
其實索引結(jié)構(gòu)就是索引類型分類,他兩本質(zhì)就是一個東西,沒有區(qū)別!

索引分類,也可以在Navcat客戶端當中,查看可選擇的索引類型!

3.2 聚集索引&二級索引
而在在InnoDB存儲引擎中,根據(jù)索引的存儲形式,又可以分為以下兩種:
聚集索引和二級索引跟我們上面說的索引類型可不是一個東西,切勿混淆了!

聚集索引選取規(guī)則:
- 如果存在主鍵,主鍵索引就是聚集索引。
- 如果不存在主鍵,將使用第一個唯一(UNIQUE)索引作為聚集索引。
- 如果表沒有主鍵,或沒有合適的唯一索引,則InnoDB會自動生成一個rowid作為隱藏的聚集索 引。
基于B+Tree的索引類型,聚集索引和二級索引的具體結(jié)構(gòu)如下:
假如有一張表,表里分別有三個字段id、name、gender,然后id為主鍵,那么他就是聚集索引,然后我們又給name也添加了一個索引,那么他就被稱為二級索引。
- 聚集索引的葉子節(jié)點下掛的是這一行的數(shù)據(jù) 。
- 二級索引的葉子節(jié)點下掛的是該字段值對應(yīng)的主鍵值。

接下來,我們來分析一下,當我們執(zhí)行如下的SQL語句時,具體的查找過程是什么樣子的。

具體過程如下:
- 由于是根據(jù)name字段進行查詢,所以先根據(jù)name='Arm’到name字段的二級索引中進行匹配查找。但是在二級索引中只能查找到 Arm 對應(yīng)的主鍵值 10。
- 由于查詢返回的數(shù)據(jù)是*,所以此時,還需要根據(jù)主鍵值10,到聚集索引中查找10對應(yīng)的記錄,最終找到10對應(yīng)的行row。
- 最終拿到這一行的數(shù)據(jù),直接返回即可。
回表查詢: 這種先到二級索引中查找數(shù)據(jù),找到主鍵值,然后再到聚集索引中根據(jù)主鍵值,獲取數(shù)據(jù)的方式,就稱之為回表查詢。
思考題:
(1)以下兩條SQL語句,那個執(zhí)行效率高? 為什么?
- select * from user where id = 10 ;
- select * from user where name = ‘Arm’ ;
備注: id為主鍵,name字段創(chuàng)建的有索引;
解答: A 語句的執(zhí)行性能要高于B 語句。
因為A語句直接走聚集索引,直接返回數(shù)據(jù)。 而B語句需要先查詢name字段的二級索引,然后再查詢聚集索引,也就是需要進行回表查詢。
(2)InnoDB主鍵索引的B+tree高度為多高呢?

關(guān)于頁相關(guān)知識不是很了解的,建議看看mysql存儲引擎篇:http://www.dbjr.com.cn/article/257845.htm
一頁最大為16KB,假設(shè)一行數(shù)據(jù)大小為1k,則一頁中可以存儲16行這樣的數(shù)據(jù)。InnoDB的指針占用6個字節(jié)的空間,主鍵假設(shè)為bigint類型,那么就是占用字節(jié)數(shù)為8。指針的數(shù)量是鍵值數(shù)量+1。
高度為2:
- 第一步:非葉子節(jié)點是不存儲數(shù)據(jù)的,那么我們可以通過已知的現(xiàn)有條件來算出葉子節(jié)點可以存儲多少key值。 n * 8 + (n + 1) * 6 = 16*1024 ,算出n約為 1170(這里的n代表的就是key值,8代表的是假設(shè)id為8字節(jié),n+1代表的是指針,6代表的是指針占用6字節(jié),16*1024代表的是一頁最大為16kb*1024字節(jié)數(shù))
- 第二步:有了key值數(shù)量后,通過key+1得出指針數(shù)量,指針數(shù)量就代表著最多有多少頁,因為本身高度為2,然后乘于頁大小,得出最大的空間為18736:1171* 16 = 18736
- 也就是說,如果樹的高度為2,假設(shè)一條數(shù)據(jù)為1kb,則可以存儲 18000 多條記錄。
高度為3:
- 1171 * 1171 * 16 = 21939856
- 有多少指針就代表有多少頁,因為我們要求的是求出最大數(shù)據(jù)量,所以一個指針肯定對應(yīng)一個頁
- 也就是說,如果樹的高度為3,則可以存儲 2200w 左右的記錄。
4. 索引語法
創(chuàng)建索引:CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name ( index_col_name,... ) ;
查看索引:SHOW INDEX FROM table_name ;
刪除索引:DROP INDEX index_name ON table_name ;
案例演示: 先來創(chuàng)建一張表 tb_user,并且查詢測試數(shù)據(jù)。
CREATE TABLE tb_user ( id INT PRIMARY KEY auto_increment COMMENT '主鍵', NAME VARCHAR ( 50 ) NOT NULL COMMENT '用戶名', phone VARCHAR ( 11 ) NOT NULL COMMENT '手機號', email VARCHAR ( 100 ) COMMENT '郵箱', profession VARCHAR ( 11 ) COMMENT '專業(yè)', age TINYINT UNSIGNED COMMENT '年齡', gender CHAR ( 1 ) COMMENT '性別 , 1: 男, 2: 女', STATUS CHAR ( 1 ) COMMENT '狀態(tài)', createtime datetime COMMENT '創(chuàng)建時間' ) COMMENT '系統(tǒng)用戶表'; INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '呂布', '17799990000', 'lvbu666@163.com', '軟件工程', 23, '1', '6', '2001-02-02 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '曹操', '17799990001', 'caocao666@qq.com', '通訊工程', 33, '1', '0', '2001-03-05 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '趙云', '17799990002', '17799990@139.com', '英語', 34, '1', '2', '2002-03-02 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '孫悟空', '17799990003', '17799990@sina.com', '工程造價', 54, '1', '0', '2001-07-02 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '花木蘭', '17799990004', '19980729@sina.com', '軟件工程', 23, '2', '1', '2001-04-22 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '大喬', '17799990005', 'daqiao666@sina.com', '舞蹈', 22, '2', '0', '2001-02-07 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '露娜', '17799990006', 'luna_love@sina.com', '應(yīng)用數(shù)學(xué)', 24, '2', '0', '2001-02-08 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '程咬金', '17799990007', 'chengyaojin@163.com', '化工', 38, '1', '5', '2001-05-23 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '項羽', '17799990008', 'xiaoyu666@qq.com', '金屬材料', 43, '1', '0', '2001-09-18 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '白起', '17799990009', 'baiqi666@sina.com', '機械工程及其自動 化', 27, '1', '2', '2001-08-16 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '韓信', '17799990010', 'hanxin520@163.com', '無機非金屬材料工 程', 27, '1', '0', '2001-06-12 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '荊軻', '17799990011', 'jingke123@163.com', '會計', 29, '1', '0', '2001-05-11 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '蘭陵王', '17799990012', 'lanlinwang666@126.com', '工程造價', 44, '1', '1', '2001-04-09 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '狂鐵', '17799990013', 'kuangtie@sina.com', '應(yīng)用數(shù)學(xué)', 43, '1', '2', '2001-04-10 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '貂蟬', '17799990014', '84958948374@qq.com', '軟件工程', 40, '2', '3', '2001-02-12 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '妲己', '17799990015', '2783238293@qq.com', '軟件工程', 31, '2', '0', '2001-01-30 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '羋月', '17799990016', 'xiaomin2001@sina.com', '工業(yè)經(jīng)濟', 35, '2', '0', '2000-05-03 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '嬴政', '17799990017', '8839434342@qq.com', '化工', 38, '1', '1', '2001-08-08 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '狄仁杰', '17799990018', 'jujiamlm8166@163.com', '國際貿(mào)易', 30, '1', '0', '2007-03-12 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '安琪拉', '17799990019', 'jdodm1h@126.com', '城市規(guī)劃', 51, '2', '0', '2001-08-15 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '典韋', '17799990020', 'ycaunanjian@163.com', '城市規(guī)劃', 52, '1', '2', '2000-04-12 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '廉頗', '17799990021', 'lianpo321@126.com', '土木工程', 19, '1', '3', '2002-07-18 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '后羿', '17799990022', 'altycj2000@139.com', '城市園林', 20, '1', '0', '2002-03-10 00:00:00' ); INSERT INTO tb_user ( NAME, phone, email, profession, age, gender, STATUS, createtime ) VALUES ( '姜子牙', '17799990023', '37483844@qq.com', '工程造價', 29, '1', '4', '2003-05-26 00:00:00' );
數(shù)據(jù)準備好了之后,接下來,我們就來完成如下需求:
- name字段為姓名字段,該字段的值可能會重復(fù),為該字段創(chuàng)建索引:CREATE INDEX idx_user_name ON tb_user(name);
- phone手機號字段的值,是非空,且唯一的,為該字段創(chuàng)建唯一索引:CREATE UNIQUE INDEX idx_user_phone ON tb_user(phone);
- 為profession、age、status創(chuàng)建聯(lián)合索引(創(chuàng)建的索引同時指定了多個列,我們稱之為聯(lián)合索引或者組合索引):CREATE INDEX idx_user_pro_age_sta ON tb_user(profession,age,status);
- 為email建立合適的索引來提升查詢效率:CREATE INDEX idx_email ON tb_user(email);
完成上述的需求之后,我們再查看tb_user表的所有的索引數(shù)據(jù):

5. SQL性能分析
5.1 SQL執(zhí)行頻率
MySQL 客戶端連接成功后,通過 show [session|global] status 命令可以提供服務(wù)器狀態(tài)信息。通過如下指令,可以查看當前數(shù)據(jù)庫的INSERT、UPDATE、DELETE、SELECT的訪問頻次:
-- session 是查看當前會話 ; -- global 是查詢?nèi)謹?shù)據(jù) ; SHOW GLOBAL STATUS LIKE 'Com_______';

- Com_delete: 刪除次數(shù)
- Com_insert: 插入次數(shù)
- Com_select: 查詢次數(shù)
- Com_update: 更新次數(shù)
我們可以在當前數(shù)據(jù)庫再執(zhí)行幾次查詢操作,然后再次查看執(zhí)行頻次,看看 Com_select 參數(shù)會不會變化。

通過上述指令,我們可以查看到當前數(shù)據(jù)庫到底是以查詢?yōu)橹?,還是以增刪改為主,從而為數(shù)據(jù)庫優(yōu)化提供參考依據(jù)。 如果是以增刪改為主,我們可以考慮不對其進行索引的優(yōu)化。 如果是以查詢?yōu)橹?,那么就要考慮對數(shù)據(jù)庫的索引進行優(yōu)化了。
那么通過查詢SQL的執(zhí)行頻次,我們就能夠知道當前數(shù)據(jù)庫到底是增刪改為主,還是查詢?yōu)橹鳌?那假如說是以查詢?yōu)橹鳎覀冇衷撊绾味ㄎ会槍τ谀切┎樵冋Z句進行優(yōu)化呢? 次數(shù)我們可以借助于慢查詢?nèi)罩尽?/p>
接下來,我們就來介紹一下MySQL中的慢查詢?nèi)罩尽?/strong>
5.2 慢查詢?nèi)罩?/h3>
慢查詢?nèi)罩居涗浟怂袌?zhí)行時間超過指定參數(shù)(long_query_time,單位:秒,默認10秒)的所有SQL語句的日志。
MySQL的慢查詢?nèi)罩灸J沒有開啟,我們可以查看一下系統(tǒng)變量 slow_query_log
show variables like 'slow_query_log';

如果要開啟慢查詢?nèi)罩?,需要在MySQL的配置文件(如果是linux則是/etc/my.cnf,如果是windows在mysql安裝目錄下的my.ini)中配置如下信息:注意配置一定要加載配置文件最后面,不然可能會出現(xiàn)問題!
這里我用的是mysql5.5版本,然后是在windows下配置的以下參數(shù),并沒有出現(xiàn)問題,如果您出現(xiàn)了問題,那么可能mysql版本之間變量名稱有變化!
ft_min_word_len=1 #慢查詢?nèi)罩敬娣怕窂降刂? log-slow-queries = D:\slow.log #慢查詢記錄的時間 long_query_time = 2
測試: 配置完畢之后,重新啟動MySQL服務(wù)器進行測試,查看慢日志文件中記錄的信息。
mysql快速生成千萬數(shù)據(jù):http://www.dbjr.com.cn/article/257848.htm
(1)執(zhí)行如下SQL語句 :
-- 這條SQL執(zhí)行效率比較高, 執(zhí)行耗時 0.00sec select * from tb_user; -- 由于t_user表中, 預(yù)先存入了700w的記錄, count一次,耗時6s select count(*) from t_user;
(2)檢查慢查詢?nèi)罩?/strong>
最終我們發(fā)現(xiàn),在慢查詢?nèi)罩局?,只會記錄?zhí)行時間超多我們預(yù)設(shè)時間(2s)的SQL,執(zhí)行較快的SQL是不會記錄的。

那這樣,通過慢查詢?nèi)罩?,就可以定位出?zhí)行效率比較低的SQL,從而有針對性的進行優(yōu)化。
5.3 profile詳情
show profiles 能夠在做SQL優(yōu)化時幫助我們了解時間都耗費到哪里去了。通過have_profiling參數(shù),能夠看到當前MySQL是否支持profile操作:SELECT @@have_profiling ;
profile查詢出來的數(shù)據(jù)每個客戶端是相互隔離的。
查看prifile是否開啟了:select @@profiling;

可以看到,當前MySQL是支持 profile操作的,但是開關(guān)是關(guān)閉的??梢酝ㄟ^set語句在session/global級別開啟profiling:
SET profiling = 1;
開關(guān)已經(jīng)打開了,接下來,我們所執(zhí)行的SQL語句,都會被MySQL記錄,并記錄執(zhí)行時間消耗到哪兒去了。 我們直接執(zhí)行如下的SQL語句:
select * from tb_user; select * from tb_user where id = 1; select * from tb_user where name = '白起'; select count(*) from t_user;
執(zhí)行一系列的業(yè)務(wù)SQL的操作,然后通過如下指令查看指令的執(zhí)行耗時:
- 查看每一條SQL的耗時基本情況:show profiles;
- 查看指定query_id的SQL語句各個階段的耗時情況:show profile for query query_id;
- 查看指定query_id的SQL語句CPU的使用情況:show profile cpu for query query_id;
查看每一條SQL的耗時情況:

查看指定SQL各個階段的耗時情況 :

5.4 explain
EXPLAIN 或者 DESC命令獲取 MySQL 如何執(zhí)行 SELECT 語句的信息,包括在 SELECT 語句執(zhí)行過程中表如何連接和連接的順序。
語法:
-- 直接在select語句之前加上關(guān)鍵字 explain / desc EXPLAIN SELECT 字段列表 FROM 表名 WHERE 條件 ;

Explain 執(zhí)行計劃中各個字段的含義:

6. 索引使用
6.1 驗證索引效率
在講解索引的使用原則之前,先通過一個簡單的案例,來驗證一下索引,看看是否能夠通過索引來提升數(shù)據(jù)查詢性能。在演示的時候,我們還是使用之前準備的一張表 t_user, 在這張表中準備了700w的記錄。
這張表中id為主鍵,有主鍵索引,而其他字段是沒有建立索引的。 我們先來查詢其中的一條記錄,看看里面的字段情況,執(zhí)行如下SQL:
可以看到即使有1000w的數(shù)據(jù),根據(jù)id進行數(shù)據(jù)查詢,性能依然很快,因為主鍵id是有索引的。 那么接下來,我們再來根據(jù) c_name字段進行查詢,執(zhí)行如下SQL:
我們可以看到根據(jù)c_name字段進行查詢,查詢返回了一條數(shù)據(jù),結(jié)果耗時 3.52 sec,就是因為c_name沒有索引,而造成查詢效率很低。
那么我們可以針對于c_name字段,建立一個索引,建立了索引之后,我們再次根據(jù)c_name進行查詢,再來看一下查詢耗時情況。
創(chuàng)建索引:create index idx_user_cname on t_user(c_name);然后再次執(zhí)行相同的SQL語句,再次查看SQL的耗時。
我們明顯會看到,c_name字段建立了索引之后,查詢性能大大提升。建立索引前后,查詢耗時都不是一個數(shù)量級的。
6.2 最左前綴法則
如果索引了多列(聯(lián)合索引),要遵守最左前綴法則。最左前綴法則指的是查詢從索引的最左列開始,并且不跳過索引中的列。如果跳躍某一列,索引將會部分失效(后面的字段索引失效)。
以 tb_user 表為例,我們先來查看一下之前 tb_user 表所創(chuàng)建的索引。

在 tb_user 表中,有一個聯(lián)合索引,這個聯(lián)合索引涉及到三個字段,順序分別為:profession,age,status。
對于最左前綴法則指的是,查詢時,最左變的列,也就是profession必須存在,否則索引全部失效。而且中間不能跳過某一列,否則該列后面的字段索引將失效。
(1)第一步我們先演示索引成功的案例,看一下具體的執(zhí)行計劃:
explain select * from tb_user where profession = '軟件工程' and age = 31 and status = '0';
explain select * from tb_user where profession = '軟件工程' and age = 31;
explain select * from tb_user where profession = '軟件工程';
以上的這三組測試中,我們發(fā)現(xiàn)只要聯(lián)合索引最左邊的字段 profession存在,索引就會生效,只不過索引的長度不同。 而且由以上三組測試,我們也可以推測出profession字段索引長度為36、age字段索引長度為2、status字段索引長度為4。我們下面主要根據(jù)這個長度來判斷組合索引當中哪個索引沒有生效!
注意:如果看不懂explain執(zhí)行計劃的,一定要看explamin介紹的參數(shù)解釋!不同的數(shù)據(jù)庫編碼以及數(shù)據(jù)庫版本可能key_len長度也會不一樣,這里我用的mysql是5.5版本,表用的是utf-8編碼
(2)下面我們進行演示最左邊的profession列沒用到的時候,索引失效的情況:
explain select * from tb_user where age = 31 and status = '0';
explain select * from tb_user where status = '0';
而通過上面的這兩組測試,我們也可以看到索引并未生效,原因是因為不滿足最左前綴法則,聯(lián)合索引最左邊的列profession不存在。
(3)下面我們進行演示跳過中間的列,索引失效的情況:
explain select * from tb_user where profession = '軟件工程' and status = '0';上述的SQL查詢時,存在profession字段,最左邊的列是存在的,索引滿足最左前綴法則的基本條件。但是查詢時,跳過了age這個列,所以后面的列索引是不會使用的,也就是索引部分生效,所以索引的長度就是36。
思考題:當執(zhí)行SQL語句:
explain select * from tb_user where age = 31 and status = '0' and profession = '軟件工程';時,是否滿足最左前綴法則,走不走上述的聯(lián)合索引,索引長度?
可以看到,是完全滿足最左前綴法則的,索引長度42,聯(lián)合索引是生效的。
注意 : 最左前綴法則中指的最左邊的列,是指在查詢時,聯(lián)合索引的最左邊的字段(即是第一個字段)必須存在,與我們編寫SQL時,條件編寫的先后順序無關(guān)。
6.3 索引失效情況
6.3.1 ><范圍查詢
聯(lián)合索引中,出現(xiàn)范圍查詢(>,<),范圍查詢右側(cè)的列索引失效。
explain select * from tb_user where profession = '軟件工程' and age > 30 and status = '0';當范圍查詢使用> 或 < 時,走聯(lián)合索引了,但是索引的長度為38,就說明范圍查詢右邊的status字段是沒有走索引的。
explain select * from tb_user where profession = '軟件工程' and age >= 30 and status = '0';
當范圍查詢使用>= 或 <= 時,走聯(lián)合索引了,但是索引的長度為42,就說明所有的字段都是走索引
的。
所以,在業(yè)務(wù)允許的情況下,盡可能的使用類似于 >= 或 <= 這類的范圍查詢,而避免使用 > 或 < 。
6.3.2 索引列運算
不要在索引列上進行運算操作, 索引將失效。
在tb_user表中,除了前面介紹的聯(lián)合索引之外,還有一個索引,是phone字段的單列索引。

當根據(jù)phone字段進行等值匹配查詢時, 索引生效。
explain select * from tb_user where phone = '17799990015';
當根據(jù)phone字段進行函數(shù)運算操作之后,索引失效。
explain select * from tb_user where substring(phone,10,2) = '15';
6.3.3 字符串不加引號
字符串類型字段使用時,不加引號,索引將失效。
接下來,我們通過兩組示例,來看看對于字符串類型的字段,加單引號與不加單引號的區(qū)別.
第一組:
explain select * from tb_user where profession = '軟件工程' and age = 31 and status = '0';explain select * from tb_user where profession = '軟件工程' and age = 31 and status = 0;
第二組:
explain select * from tb_user where phone = '17799990015';explain select * from tb_user where phone = 17799990015;
6.3.4 模糊查詢
如果僅僅是尾部模糊匹配,索引不會失效。如果是頭部模糊匹配,索引失效。
接下來,我們來看一下這三條SQL語句的執(zhí)行效果,查看一下其執(zhí)行計劃:
由于下面查詢語句中,都是根據(jù)profession字段查詢,符合最左前綴法則,聯(lián)合索引是可以生效的,我們主要看一下,模糊查詢時,%加在關(guān)鍵字之前,和加在關(guān)鍵字之后的影響。
explain select * from tb_user where profession like ‘軟件%'; explain select * from tb_user where profession like ‘%工程'; explain select * from tb_user where profession like ‘%工%';

經(jīng)過上述的測試,我們發(fā)現(xiàn),在like模糊查詢中,在關(guān)鍵字后面加%,索引可以生效。而如果在關(guān)鍵字前面加了%,索引將會失效。
6.3.5 or連接條件
用or分割開的條件, 如果or前的條件中的列有索引,而后面的列中沒有索引,那么涉及的索引都不會被用到。
explain select * from tb_user where id = 10 or age = 23; explain select * from tb_user where phone = ‘17799990017' or age = 23;

由于age沒有索引,所以即使id、phone有索引,索引也會失效。所以需要針對于age也要建立索引。
然后,我們可以對age字段建立索引:create index idx_user_age on tb_user(age);
建立了索引之后,我們再次執(zhí)行上述的SQL語句,看看前后執(zhí)行計劃的變化。

這里我發(fā)現(xiàn)一個問題,我用的mysql5.5版本or不管兩邊是否都有索引,直接都不會走索引,但是又用了mysql 8測試了一下,.當age沒有索引的時候不走索引,當兩邊都有索引的時候確實會走索引。
6.3.6 數(shù)據(jù)分布影響
如果MySQL評估使用索引比全表更慢,則不使用索引。
explain select * from tb_user where phone >= '17799990005'; explain select * from tb_user where phone >= '17799990015';
mysql5.5版本執(zhí)行如下:

mysql8.0版本執(zhí)行如下:

經(jīng)過測試我們發(fā)現(xiàn),在mysql8版本當中,相同的SQL語句,只是傳入的字段值不同,最終的執(zhí)行計劃完全不一樣,這是為什么呢?
就是因為MySQL 8 版本 在查詢時,會評估使用索引的效率與走全表掃描的效率,如果走全表掃描更快,則放棄索引,走全表掃描。 因為索引是用來索引少量數(shù)據(jù)的,如果通過索引查詢返回大批量的數(shù)據(jù),則還不如走全表掃描來的快,此時索引就會失效。
接下來,我們再來看看 is null 與 is not null 操作是否走索引。
explain select * from tb_user where profession is null; explain select * from tb_user where profession is not null;

接下來,我們做一個操作將profession字段值全部更新為null:update tb_user set profession = null;
然后,再次執(zhí)行上述的兩條SQL,查看SQL語句的執(zhí)行計劃。

最終我們看到,一模一樣的SQL語句,先后執(zhí)行了兩次,結(jié)果查詢計劃是不一樣的,為什么會出現(xiàn)這種現(xiàn)象,這是和數(shù)據(jù)庫的數(shù)據(jù)分布有關(guān)系。查詢時MySQL會評估,走索引快,還是全表掃描快,如果全表掃描更快,則放棄索引走全表掃描。 因此,is null 、is not null是否走索引,得具體情況具體分析,并不是固定的。
6.4 SQL提示
目前tb_user表的索引情況如下:

(1)執(zhí)行SQL :
explain select * from tb_user where profession = '軟件工程';
查詢走了聯(lián)合索引。
(2)執(zhí)行SQL,創(chuàng)建profession的單列索引:
create index idx_user_pro on tb_user(profession);
創(chuàng)建單列索引后,再次執(zhí)行A中的SQL語句,查看執(zhí)行計劃,看看到底走哪個索引。
測試結(jié)果,我們可以看到,possible_keys中 idx_user_pro_age_sta,idx_user_pro 這兩個索引都可能用到,最終MySQL選擇了idx_user_pro_age_sta索引。這是MySQL自動選擇的結(jié)果。
那么,我們能不能在查詢的時候,自己來指定使用哪個索引呢? 答案是肯定的,此時就可以借助于MySQL的SQL提示來完成。 接下來,介紹一下SQL提示。
SQL提示,是優(yōu)化數(shù)據(jù)庫的一個重要手段,簡單來說,就是在SQL語句中加入一些人為的提示來達到優(yōu)化操作的目的。
use index : 建議MySQL使用哪一個索引完成此次查詢(僅僅是建議,mysql內(nèi)部還會再次進行評估)。
explain select * from tb_user use index(idx_user_pro) where profession = '軟件工 程';
示例演示:
ignore index : 忽略指定的索引。
explain select * from tb_user ignore index(idx_user_pro) where profession = '軟件工 程';
示例演示:
force index : 強制使用索引。
explain select * from tb_user force index(idx_user_pro) where profession = '軟件工 程';
示例演示:
6.5 覆蓋索引
盡量使用覆蓋索引,減少select *。 那么什么是覆蓋索引呢? 覆蓋索引是指 查詢使用了索引,并且需要返回的列,在該索引中已經(jīng)全部能夠找到 。說白了就是避免回表查詢
接下來,我們來看一組SQL的執(zhí)行計劃,看看執(zhí)行計劃的差別,然后再來具體做一個解析。
explain select id, profession from tb_user where profession = ‘軟件工程' and age = 31 and status = ‘0' ; explain select id,profession,age, status from tb_user where profession = ‘軟件工程' and age = 31 and status = ‘0' ; explain select id,profession,age, status, name from tb_user where profession = ‘軟 件工程' and age = 31 and status = ‘0' ; explain select * from tb_user where profession = ‘軟件工程' and age = 31 and status = ‘0';
mysql5.5執(zhí)行結(jié)果:

mysql8.0執(zhí)行結(jié)果:

從上述的執(zhí)行計劃我們可以看到,這四條SQL語句的執(zhí)行計劃前面所有的指標都是一樣的,看不出來差異。但是此時,我們主要關(guān)注的是后面的Extra。
在5.5版本當中,前面兩條SQL的結(jié)果為 Using where; Using Index ; 而后面兩條SQL的結(jié)果為: Using where 。
在8.0版本當中,前面兩條SQL的結(jié)果為 Using Index ; 而后面兩條SQL的結(jié)果為: NULL 。

因為,在tb_user表中有一個聯(lián)合索引 idx_user_pro_age_sta,該索引關(guān)聯(lián)了三個字段profession、age、status,而這個索引也是一個二級索引,所以葉子節(jié)點下面掛的是這一行的主鍵id。 所以當我們查詢返回的數(shù)據(jù)在 id、profession、age、status 之中,則直接走二級索引直接返回數(shù)據(jù)了。 如果超出這個范圍,就需要拿到主鍵id,再去掃描聚集索引,再獲取額外的數(shù)據(jù)了,這個過程就是回表。 而我們?nèi)绻恢笔褂胹elect * 查詢返回所有字段值,很容易就會造成回表查詢(除非是根據(jù)主鍵查詢,此時只會掃描聚集索引)。
表結(jié)構(gòu)及索引示意圖:

id是主鍵,是一個聚集索引。 name字段建立了普通索引,是一個二級索引(輔助索引)。
執(zhí)行SQL : select * from tb_user where id = 2;

根據(jù)id查詢,直接走聚集索引查詢,一次索引掃描,直接返回數(shù)據(jù),性能高。
執(zhí)行SQL:selet id,name from tb_user where name = 'Arm';

執(zhí)行SQL:selet id,name,gender from tb_user where name = 'Arm';

由于在name的二級索引中,不包含gender,所以,需要兩次索引掃描,也就是需要回表查詢,性能相對較差一點。
思考題: 一張表, 有四個字段(id, username, password, status), 由于數(shù)據(jù)量大, 需要對以下SQL語句進行優(yōu)化, 該如何進行才是最優(yōu)方案:
select id,username,password from tb_user where username ='zhangsan';
答案: 針對于 username, password建立聯(lián)合索引, sql為:create index idx_user_name_pass on tb_user(username,password);
這樣可以避免上述的SQL語句,在查詢的過程中,出現(xiàn)回表查詢。
6.6 前綴索引
當字段類型為字符串(varchar,text,longtext等)時,有時候需要索引很長的字符串,這會讓索引變得很大,查詢時,浪費大量的磁盤IO, 影響查詢效率。此時可以只將字符串的一部分前綴,建立索引,這樣可以大大節(jié)約索引空間,從而提高索引效率。
語法:create index idx_xxxx on table_name(column(n)) ;
示例: 為tb_user表的email字段,建立長度為5的前綴索引。
create index idx_email_5 on tb_user(email(5));

可以根據(jù)索引的選擇性來決定,而選擇性是指不重復(fù)的索引值(基數(shù))和數(shù)據(jù)表的記錄總數(shù)的比值,索引選擇性越高則查詢效率越高, 唯一索引的選擇性是1,這是最好的索引選擇性,性能也是最好的。
select count(distinct email) / count(*) from tb_user ; select count(distinct substring(email,1,5)) / count(*) from tb_user ;
前綴索引的查詢流程

6.7 單列索引與聯(lián)合索引
- 單列索引:即一個索引只包含單個列。
- 聯(lián)合索引:即一個索引包含了多個列。
我們先來看看 tb_user 表中目前的索引情況:在查詢出來的索引中,既有單列索引,又有聯(lián)合索引。

接下來,我們來執(zhí)行一條SQL語句,看看其執(zhí)行計劃:

通過上述執(zhí)行計劃我們可以看出來,在and連接的兩個字段 phone、name上都是有單列索引的,但是最終mysql只會選擇一個索引,也就是說,只能走一個字段的索引,此時是會回表查詢的。
緊接著,我們再來創(chuàng)建一個phone和name字段的聯(lián)合索引來查詢一下執(zhí)行計劃。

此時,查詢時,就走了聯(lián)合索引,而在聯(lián)合索引中包含 phone、name的信息,在葉子節(jié)點下掛的是對應(yīng)的主鍵id,所以查詢是無需回表查詢的。
在業(yè)務(wù)場景中,如果存在多個查詢條件,考慮針對于查詢字段建立索引時,建議建立聯(lián)合索引,而非單列索引。
如果查詢使用的是聯(lián)合索引,具體的結(jié)構(gòu)示意圖如下:

7. 索引設(shè)計原則
- 針對于數(shù)據(jù)量較大,且查詢比較頻繁的表建立索引。
- 針對于常作為查詢條件(where)、排序(order by)、分組(group by)操作的字段建立索引。
- 盡量選擇區(qū)分度高的列作為索引,盡量建立唯一索引,區(qū)分度越高,使用索引的效率越高。
- 如果是字符串類型的字段,字段的長度較長,可以針對于字段的特點,建立前綴索引。
- 盡量使用聯(lián)合索引,減少單列索引,查詢時,聯(lián)合索引很多時候可以覆蓋索引,節(jié)省存儲空間,避免回表,提高查詢效率。
- 要控制索引的數(shù)量,索引并不是多多益善,索引越多,維護索引結(jié)構(gòu)的代價也就越大,會影響增刪改的效率。
create unique index idx_user_phone_name on tb_user(phone,name); - 如果索引列不能存儲NULL值,請在創(chuàng)建表時使用NOT NULL約束它。當優(yōu)化器知道每列是否包含NULL值時,它可以更好地確定哪個索引最有效地用于查詢。
本篇要求掌握:
- 什么是索引?
- 索引是個什么樣的數(shù)據(jù)結(jié)構(gòu)呢?
- 為什么使用索引?
- Hash 索引和 B+ 樹索引有什么區(qū)別或者說優(yōu)劣呢?
- 什么是聚簇索引(一個)
- 說一說索引的底層實現(xiàn)?(一般就是指的B+Tree)
- 索引有哪些優(yōu)缺點?
- 聚簇索引和非聚簇索引的區(qū)別(非聚簇索引就是二級索引)
- MySQL中有幾種索引類型,可以簡單說說嗎?(主鍵,唯一,常規(guī)(常規(guī)又分為了多列和單列,多列的一般稱之為組合索引),全文)
- 覆蓋索引是什么? (覆蓋索引指的就是查詢的列盡量是索引所覆蓋的列,這樣可以避免回表查)
- 非聚簇索引一定會回表查詢嗎?(假如恰好select的列是條件當中用到的索引列,是不用回表的)
- 聯(lián)合索引是什么?為什么需要注意聯(lián)合索引中的順序?(最左前綴法則)
- 創(chuàng)建的索引有沒有被使用到?或者說怎么才可以知道這條語句運行很慢的原因?(使用explain)
- 索引什么情況下會失效?
- 為什么Mysql用B+樹做索引而不用B-樹或紅黑樹、二叉樹?
- 索引在什么情況下遵循最左前綴的規(guī)則?(組合索引)
總結(jié)
到此這篇關(guān)于Mysql索引(index)的文章就介紹到這了,更多相關(guān)Mysql索引詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
連接遠程mysql數(shù)據(jù)庫失敗常見原因及解決方案
這篇文章主要介紹了連接遠程mysql數(shù)據(jù)庫失敗常見原因及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
MySQL 百萬級數(shù)據(jù)的4種查詢優(yōu)化方式
本文講解了MySQL 百萬級數(shù)據(jù)的4種查詢優(yōu)化方式,大家可以根據(jù)自身需求,選擇適合自己的優(yōu)化方式2021-06-06
linux環(huán)境下配置mysql5.6支持IPV6連接的方法
本文主要介紹在linux系統(tǒng)下,如何配置mysql支持IPV6的連接,本文圖文并茂給大家介紹的非常詳細,具有參考借鑒價值,需要的朋友參考下吧2018-01-01
MySQL因大事務(wù)導(dǎo)致的Insert慢實例分析
這篇文章主要給大家介紹了關(guān)于MySQL因大事務(wù)導(dǎo)致Insert慢的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10











上述的SQL查詢時,存在profession字段,最左邊的列是存在的,索引滿足最左前綴法則的基本條件。但是查詢時,跳過了age這個列,所以后面的列索引是不會使用的,也就是索引部分生效,所以索引的長度就是36。
當范圍查詢使用> 或 < 時,走聯(lián)合索引了,但是索引的長度為38,就說明范圍查詢右邊的status字段是沒有走索引的。










