<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          圖解索引

          共 5620字,需瀏覽 12分鐘

           ·

          2021-11-15 01:32

          在下方公眾號后臺回復(fù):面試手冊,可獲取杰哥匯總的 3 份面試 PDF 手冊。

          什么是索引?

          索引是輔助存儲引擎高效獲取數(shù)據(jù)的一種數(shù)據(jù)結(jié)構(gòu)。

          很多人形象的說索引就是數(shù)據(jù)的目錄,便于存儲引擎快速的定位數(shù)據(jù)。

          索引的分類

          我們經(jīng)常從以下幾個方面對索引進(jìn)行分類

          從數(shù)據(jù)結(jié)構(gòu)的角度對索引進(jìn)行分類

          • B+tree

          • Hash

          • Full-texts索引

          從物理存儲的角度對索引進(jìn)行分類

          • 聚簇索引

          • 二級索引(輔助索引)

          從索引字段特性角度分類

          • 主鍵索引

          • 唯一索引

          • 普通索引

          • 前綴索引

          從組成索引的字段個數(shù)角度分類

          • 單列索引

          • 聯(lián)合索引(復(fù)合索引)

          數(shù)據(jù)結(jié)構(gòu)角度看索引

          下表是MySQL常見的存儲引擎InnoDB,MyISAM和Memory分別支持的索引類型


          InnoDBMyISAMMemory
          B+tree索引YesYesYes
          Hash索引NoNoYes
          Full-text索引YesYesNo

          在實際使用中,InnoDB作為MySQL建表時默認(rèn)的存儲引擎

          對上表進(jìn)行橫向查看可以了解到,B+tree是MySQL中被存儲引擎采用最多的索引類型。

          這里淺嘗輒止的談一下B+tree 與 Hash和紅黑樹的區(qū)別。

          B+tree和B-tree

          1970年,R.Bayer和E.Mccreight提出了一種適用于外查找的平衡多叉樹——B-樹,磁盤管理系統(tǒng)中的目錄管理,以及數(shù)據(jù)庫系統(tǒng)中的索引組織多數(shù)采用B-Tree這種數(shù)據(jù)結(jié)構(gòu)。--數(shù)據(jù)結(jié)構(gòu)C語言版第二版 嚴(yán)蔚敏

          B+tree是B-Tree的一個變種。(哦,對了,B-tree念B樹,它不叫B減樹。。。)

          B+tree只在葉子節(jié)點存儲數(shù)據(jù),而B-tree非葉子節(jié)點也存儲數(shù)據(jù)。

          因此,B+tree單個節(jié)點的數(shù)量更小,在相同的磁盤IO下能查詢更多的節(jié)點。

          另外B+tree葉子節(jié)點采用單鏈表鏈接適合MySQL中常見的基于范圍的順序檢索場景,而B-tree無法做到這一點。

          B+tree和紅黑樹

          對于有N個葉子節(jié)點的B+tree,搜索復(fù)雜度為O(logdN) ,d是指degree是指B+tree的度,表示節(jié)點允許的最大子節(jié)點個數(shù)為d個,在實際的運用中d值是大于100的,即使數(shù)據(jù)達(dá)到千萬級別時候B+tree的高度依然維持在3-4左右,保證了3-4次磁盤I/O就能查到目標(biāo)數(shù)據(jù).

          從上圖中可以看出紅黑樹是二叉樹,節(jié)點的子節(jié)點個數(shù)最多為2個,意味著其搜索復(fù)雜度為O(logN),比B+樹高出不少,因此紅黑樹檢索到目標(biāo)數(shù)據(jù)所需經(jīng)理的磁盤I/O次數(shù)更多。

          B+tree索引與Hash表

          范圍查詢是MySQL數(shù)據(jù)庫中常見的場景,而Hash表不適合做范圍查詢,Hash表更適合做等值查詢,另外Hash表還存在Hash函數(shù)選擇和Hash值沖突等問題。

          因為這些原因,B+tree索引要比Hash表索引有更廣的適用場景。

          物理存儲角度看索引

          MySQL中的兩種常用存儲引擎對索引的處理方式差別較大。

          InnoDB的索引

          首先看一下InnoDB存儲引擎中的索引,InnoDB表的索引按照葉子節(jié)點存儲的是否為完整表數(shù)據(jù)分為聚簇索引和二級索引。

          全表數(shù)據(jù)就是存儲在聚簇索引中的。

          聚簇索引以外的其它索引叫做二級索引。

          下面結(jié)合實際的例子介紹下這兩類索引。

          create?table?workers
          ?(
          ?????id????int(11)?????not?null?auto_increment?comment?'員工工號',
          ?????name??varchar(16)?not?null?comment?'員工名字',
          ?????sales?int(11)?default?null?comment?'員工銷售業(yè)績',
          ?????primary?key?(id)
          ?)?engine?InnoDB
          ???AUTO_INCREMENT?=?10
          ???default?charset?=?utf8;

          ?insert?into?workers(id,?name,?sales)?values?(1,?'江南',?12744);
          ?insert?into?workers(id,?name,?sales)?values?(3,?'今何在',?14082);
          ?insert?into?workers(id,?name,?sales)?values?(7,?'路明非',?14738);
          ?insert?into?workers(id,?name,?sales)?values?(8,?'呂歸塵',?7087);
          ?insert?into?workers(id,?name,?sales)?values?(11,?'姬野',?8565);
          ?insert?into?workers(id,?name,?sales)?values?(15,?'凱撒',?8501);
          ?insert?into?workers(id,?name,?sales)?values?(20,?'繪梨衣',?7890);

          我們現(xiàn)在自己的測試數(shù)據(jù)庫中創(chuàng)建一個包含銷售員信息的測試表workers

          包含id(主鍵),name,sales三個字段,指定表的存儲引擎為InnoDB。

          然后插入8條數(shù)據(jù)

          這個例子當(dāng)中,workers表的聚簇索引建立在字段id上

          為了準(zhǔn)確模擬,我們先把主鍵id插入b+tree得到下圖

          然后在此圖基礎(chǔ)上,我畫出了高清版。

          從圖中可以看到,聚簇索引的每個葉子節(jié)點存儲了一行完整的表數(shù)據(jù),葉子節(jié)點間采用單向鏈表按id列遞增連接,可以方便的進(jìn)行順序檢索。

          InnoDB表要求必須有聚簇索引,默認(rèn)在主鍵字段上建立聚簇索引,在沒有主鍵字段的情況下,表的第一個NOT NULL 的唯一索引將被建立為聚簇索引,在前兩者都沒有的情況下,InnoDB將自動生成一個隱式自增id列并在此列上創(chuàng)建聚簇索引。

          接著來看二級索引。

          還以剛才的workers表為例

          我們在name字段上添加二級索引index_name

          alter?table??workers?add?index??index_name(name);

          同樣我們畫出了二級索引index_name的B+tree示意圖

          圖中可以看出二級索引的葉子節(jié)點并不存儲一行完整的表數(shù)據(jù),而是存儲了聚簇索引所在列的值,也就是

          workers表中的id列的值。

          這兩張示意圖中B+tree的度設(shè)置為了3 ,這也主要是為了方便演示。

          實際的B+tree索引中,樹的度通常會大于100。

          說了聚簇索引和二級索引 肯定要提到回表查詢。

          由于二級索引的葉子節(jié)點不存儲完整的表數(shù)據(jù),所以當(dāng)通過二級索引查詢到聚簇索引的列值后,還需要回到局促索引也就是表數(shù)據(jù)本身進(jìn)一步獲取數(shù)據(jù)。

          比如說我們要在workers表中查詢 名叫呂歸塵的人

          select?*?from?workers?where?name='呂歸塵';

          這條sql通過name='呂歸塵'的條件

          在二級索引index_name中查詢到主鍵id=8 ,接著帶著id=8這個條件

          進(jìn)一步回到聚簇索引查詢以后才能獲取到完整的數(shù)據(jù),很顯然回表需要額外的B+tree搜索過程,必然增大查詢耗時。

          需要注意的是通過二級索引查詢時,回表不是必須的過程,當(dāng)Query的所有字段在二級索引中就能找到時,就不需要回表,MySQL稱此時的二級索引為覆蓋索引或稱觸發(fā)了索引覆蓋。

          select?id,name?from?workers?where?name='呂歸塵';

          這句sql只查詢了id,和name,二級索引就已經(jīng)包含了Query所以需要的所有字段,就無需回表查詢。

          explain?select?id,name?from?workers?where?name='呂歸塵';

          使用explain查看此條sql的執(zhí)行計劃

          執(zhí)行計劃的Extra字段中出現(xiàn)了Using where;Using index表明查詢觸發(fā)了索引index_name的索引覆蓋,且對索引做了where篩選,這里不需要回表。

          下面做對比,查詢一下沒有索引的

          explain?select?id,name,sales?from?workers?where?name='呂歸塵';

          Extra為Using Index Condition 表示會先條件過濾索引,過濾完索引后找到所有符合索引條件的數(shù)據(jù)行,隨后用 WHERE 子句中的其他條件去過濾這些數(shù)據(jù)行。Index Condition Pushdown (ICP)是MySQL 5.6 以上版本中的新特性,是一種在存儲引擎層使用索引過濾數(shù)據(jù)的一種優(yōu)化方式。ICP開啟時的執(zhí)行計劃含有 Using index condition 標(biāo)示 ,表示優(yōu)化器使用了ICP對數(shù)據(jù)訪問進(jìn)行優(yōu)化。

          如果你對此感興趣去查閱對應(yīng)的官方文檔和技術(shù)博客。

          這次我們簡化來理解,不考慮ICP對數(shù)據(jù)訪問的優(yōu)化,

          當(dāng)關(guān)閉ICP時,Index僅僅是data access的一種訪問方式,存儲引擎通過索引回表獲取的數(shù)據(jù)會傳遞到MySQL Server 層進(jìn)行WHERE條件過濾。

          Extra為Using where 只是提醒我們MySQL將用where子句來過濾結(jié)果集。這個一般發(fā)生在MySQL服務(wù)器,而不是存儲引擎層。一般發(fā)生在不能走索引掃描的情況下或者走索引掃描,但是有些查詢條件不在索引當(dāng)中的情況下。

          這里表明沒有觸發(fā)索引覆蓋,進(jìn)行回表查詢。

          MyISAM的索引

          說完了InnoDB的索引,接下來我們來看MyISAM的索引

          以MyISAM存儲引擎存儲的表不存在聚簇索引。

          MyISAM索引B+tree示意圖

          MyISAM表中的主鍵索引和非主鍵索引的結(jié)構(gòu)是一樣的,從上圖中我們可以看到

          他們的葉子節(jié)點是不存儲表數(shù)據(jù)的,節(jié)點中存放的是表數(shù)據(jù)的地址,所以MyISAM表可以沒有主鍵。

          MyISAM表的數(shù)據(jù)和索引是分開的,是單獨存放的。

          MyISAM表中的主鍵索引和非主鍵索引的區(qū)別僅在于主鍵索引B+tree上的key必須符合主鍵的限制,

          非主鍵索引B+tree上的key只要符合相應(yīng)字段的特性就可以了。

          索引字段特性角度看索引

          主鍵索引

          • 建立在主鍵字段上的索引

          • 一張表最多只有一個主鍵索引

          • 索引列值不允許為null

          • 通常在創(chuàng)建表的時候一起創(chuàng)建

          唯一索引

          • 建立在UNIQUE字段上的索引就是唯一索引

          • 一張表可以有多個唯一索引,索引列值允許為null

          我們演示創(chuàng)建索引

          create?table?persons
          ?(
          ?????id???int(11)?not?null?auto_increment?comment?'主鍵id',
          ?????eno??int(11)?comment?'工號',
          ?????eid??int(11)?comment?'身份證號',
          ?????veid?int(11)?comment?'虛擬身份證號',
          ?????name?varchar(16)?comment?'名字',
          ?????primary?key?(id)?comment?'主鍵索引',
          ?????UNIQUE?key?(eno)?comment?'eno唯一索引',
          ?????UNIQUE?key?(eid)?comment?'eid唯一索引'
          ?)?engine?=?InnoDB
          ???auto_increment?=?1000
          ???default?charset?=?utf8;

          ?alter?table?persons
          ?????add?unique?index?index_veid?(veid)?comment?'veid唯一索引';

          通過show index from persons;命令我們看到已經(jīng)成功創(chuàng)建了三個唯一索引。

          普通索引

          主鍵索引和唯一索引對字段的要求是要求字段為主鍵或unique字段,

          而那些建立在普通字段上的索引叫做普通索引,既不要求字段為主鍵也不要求字段為unique。

          前綴索引

          前綴索引是指對字符類型字段的前幾個字符或?qū)ΧM(jìn)制類型字段的前幾個bytes建立的索引,而不是在整個字段上建索引。

          例如,可以對persons表中的name(varchar(16))字段 中name的前5個字符建立索引。

          create?index?index_name?on?persons?(name(5))?comment?'前綴索引';
          ?show?index?from?persons;

          前綴索引可以建立在類型為

          • char

          • varchar

          • binary

          • varbinary

          的列上,可以大大減少索引占用的存儲空間,也能提升索引的查詢效率。

          索引列的個數(shù)角度看索引

          • 建立在單個列上的索引為單列索引

          • 建立在多列上的稱為聯(lián)合索引(復(fù)合索引)

          演示一下聯(lián)合索引

          create?index?index_id_name?on?workers(id,name)?comment?'組合索引';

          這條語句在我們演示表workers中建立id,name這兩個字段的聯(lián)合索引。

          借助show index命令查看索引的詳細(xì)信息 操作后結(jié)果如下:

          雖然詳細(xì)信息當(dāng)中列出了兩條關(guān)于聯(lián)合索引的條目,但并不表示聯(lián)合索引是建立了多個索引,聯(lián)合索引是一個索引結(jié)構(gòu),這兩個條目表示的是組合索引中字段的具體信息,按建立索引時的書寫順序排序。

          同樣我們來看下聯(lián)合索引的B+tree示意圖

          從圖中看到組合索引的非葉子節(jié)點保存了兩個字段的值作為B+tree的key值,當(dāng)B+tree上插入數(shù)據(jù)時,先按字段id比較,在id相同的情況下按name字段比較。

          推薦閱讀

          MySQL 時間類型 datetime、bigint、timestamp,選哪個?

          MySQL 大批量插入,如何過濾掉重復(fù)數(shù)據(jù)?

          MySQL 主從復(fù)制解決了什么問題?出現(xiàn)同步延遲如何解決?

          MySQL 定時備份數(shù)據(jù)庫(非常全)

          MySQL 高頻面試題!

          瀏覽 42
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产无码做爱视频 | AV软件在线免费观看 | 婷婷五月丁香亚洲 | 先锋影音av资源网 | 最新一区二区 |