mysql的索引呢唆樊?你又知道多少?

mysql的索引呢刻蟹?你又知道多少逗旁?

在Java面試中必問mysql,問mysql的時(shí)候索引也是必問舆瘪,可見索引有多么重要片效。簡單的說索引是一種為了提高數(shù)據(jù)檢索效率的一種數(shù)據(jù)結(jié)構(gòu)。

索引的常?模型

索引的出現(xiàn)是為了實(shí)現(xiàn)數(shù)據(jù)檢索的高效介陶,只所以引入索引的概念是為因?yàn)槟軐?shí)現(xiàn)數(shù)據(jù)高效索引的數(shù)據(jù)結(jié)構(gòu)很多堤舒,我們先看一下常見的三種數(shù)據(jù)結(jié)構(gòu)哈希表、有序數(shù)組和搜索樹哺呜。

  1. 哈希表是一種key-value鍵值對(duì)舌缤,輸入key就可以找到value的值。實(shí)現(xiàn)一個(gè)哈希表很簡單某残,把值放在數(shù)組里国撵,用一個(gè)哈希函數(shù)把key換算成一個(gè)確定的位置,然后把value放在數(shù)組的這個(gè)位置玻墅。不可避免地介牙,多個(gè)key值經(jīng)過哈希函數(shù)的換算,會(huì)出現(xiàn)同一個(gè)值的情況澳厢。處理這種情況的一種方法是环础,拉出一個(gè)鏈表。假設(shè)你現(xiàn)在在維護(hù)一個(gè)訂單信息和訂單號(hào)sn的表剩拢,需要根據(jù)訂單號(hào)sn查找訂單信息线得。此時(shí)對(duì)應(yīng)的哈希索引如下圖:
    截屏2020-07-28 下午11.20.32

訂單m和訂單n算出來的都是m,但是沒關(guān)系徐伐,后面跟的是一個(gè)鏈表贯钩,如果要查詢order_m首先計(jì)算order_sn算出為m時(shí)間復(fù)雜度為O(1)再遍歷后面的鏈表時(shí)間復(fù)雜度為O(n),這種情況對(duì)于取區(qū)間的值就比較慢了,必須一個(gè)一個(gè)的遍歷。所以哈希表只適合等值查詢的場景角雷。

  1. 有序數(shù)組在等值查詢和范圍查詢場景中的性能就都非常優(yōu)秀祸穷,但是有序數(shù)組索引只適用于靜態(tài)存儲(chǔ)引擎,因?yàn)榫S持有序的成本非常高勺三,每次插入和刪除都需要維持有序雷滚,下標(biāo)需要移動(dòng)。如果僅僅看查詢效率吗坚,有序數(shù)組就是最好的數(shù)據(jù)結(jié)構(gòu)了揭措。在需要更新數(shù)據(jù)的時(shí)候就麻煩了,你往中間插入一個(gè)記錄就必 須得挪動(dòng)后面所有的記錄刻蚯,成本太高绊含。

    截屏2020-07-28 下午11.38.13

  2. 跳表 skiplist,我們知道鏈表在每次查找的時(shí)候都需要遍歷一次表炊汹,我們能不能改造一下鏈表提高檢索的效率躬充,可以使用索引的思想對(duì)鏈表進(jìn)行改造,將部分關(guān)鍵提取出來當(dāng)做關(guān)鍵節(jié)點(diǎn)讨便,如果檢索效率還是太低充甚,再將關(guān)鍵節(jié)點(diǎn)的數(shù)據(jù)取出來當(dāng)關(guān)鍵節(jié)點(diǎn)的關(guān)鍵節(jié)點(diǎn)。

    image-20200729230340082

  3. 二叉搜索樹霸褒,如果我們有個(gè)用戶表用二叉搜索樹實(shí)現(xiàn)的話如下圖:
    image.png

    二叉搜索樹的特點(diǎn)是:每個(gè)節(jié)點(diǎn)的左兒子小于父節(jié)點(diǎn)伴找,父節(jié)點(diǎn)又小于右兒子。這樣如果你要查ID_card_n2的話废菱,按照?qǐng)D中的搜索順序就是按照UserA -> UserC -> UserF -> User2這個(gè)路徑得到技矮。這個(gè)時(shí)間復(fù)雜度是O(log(N))。

    用一張動(dòng)圖來表示搜索過程假設(shè)在這個(gè)樹中搜索20流程如下:


    image.png

    當(dāng)然為了維持O(log(N))的查詢復(fù)雜度殊轴,你就需要保持這棵樹是平衡二叉樹衰倦。為了做這個(gè)保證,更新的時(shí)間復(fù)雜度也是O(log(N))旁理。所謂維持平衡就是要避免出現(xiàn)類似于鏈表的樹樊零,要讓數(shù)據(jù)分布在樹的兩側(cè)。樹可以有二叉孽文,也可以有多叉驻襟。多叉樹就是每個(gè)節(jié)點(diǎn)有多個(gè)兒子,兒子之間的大小保證從左到右遞增芋哭。二叉樹是搜索效率最高的沉衣,但是實(shí)際上大多數(shù)的數(shù)據(jù)庫存儲(chǔ)卻并不使用二叉樹。其原因是楷掉,索引不止存在內(nèi)存中厢蒜,還要寫到磁盤上。你可以想象一下一棵100萬節(jié)點(diǎn)的平衡二叉樹烹植,樹高20斑鸦。一次查詢可能需要訪問20個(gè)數(shù)據(jù)塊。為什么樹高是1就要io一次草雕?因?yàn)槊恳淮蝘o只能取出一個(gè)節(jié)點(diǎn)的數(shù)據(jù)對(duì)于二叉樹也就是2個(gè)巷屿,只取兩個(gè)因?yàn)橐鶕?jù)取出來的數(shù)據(jù)的指針去獲取后面的數(shù)據(jù),除非后面節(jié)點(diǎn)的數(shù)據(jù)剛好和硬盤中取出的數(shù)據(jù)相鄰墩虹。為了減少io成本就應(yīng)該把樹高降低嘱巾,所以數(shù)據(jù)庫使用了N插樹。在一次io中查出來一個(gè)節(jié)點(diǎn)的數(shù)據(jù)包含N個(gè)指針诫钓,從而降低io次數(shù)旬昭。

InnoDB 的索引模型

InnoDB使用了B+樹索引模型,B+樹是在搜索樹的基礎(chǔ)上改進(jìn)的菌湃。為了方便理解我們就把模型簡化為二叉樹问拘,樹中的節(jié)點(diǎn)并不存儲(chǔ)數(shù)據(jù)本身,而是只是作為索引惧所。除此之外骤坐,我們把每個(gè)葉子節(jié)點(diǎn)串在一條鏈表上,鏈表中的數(shù)據(jù)是從小到大有序的下愈。經(jīng)過改造之后的二叉樹纽绍,就像圖中這樣,看起來有點(diǎn)想跳表势似。


image.png

改造之后拌夏,如果我們要求某個(gè)區(qū)間的數(shù)據(jù)。我們只需要拿區(qū)間的起始值履因,在樹中進(jìn)行查找辖佣,當(dāng)查找到某個(gè)葉子節(jié)點(diǎn)之后,我們?cè)夙樦湵硗蟊闅v搓逾,直到鏈表中 的結(jié)點(diǎn)數(shù)據(jù)值大于區(qū)間的終止值為止卷谈。所有遍歷到的數(shù)據(jù),就是符合區(qū)間值的所有數(shù)據(jù)霞篡。但是如果在千萬上億級(jí)別的數(shù)據(jù)中構(gòu)建索引世蔗,如果索引都放入內(nèi)存中盡管檢索非常好但是也是非常耗費(fèi)內(nèi)存的一件事,所以不能不索引全部放內(nèi)存中朗兵,放硬盤就涉及到了io性能的問題污淋,前面我們提到了降低樹高,將二叉變?yōu)镸叉余掖,這個(gè)M的數(shù)值放多少合適寸爆?不管是內(nèi)存中的數(shù)據(jù),還是磁盤中的數(shù)據(jù),操作系統(tǒng)都是按頁(一頁大小通常是4KB赁豆,這個(gè)值可以通過getconfig PAGE_SIZE命令查看)來讀取的仅醇,一次會(huì)讀一頁的數(shù)據(jù)。如果要讀取的數(shù)據(jù)量超過一頁的大小魔种,就會(huì)觸發(fā)多次IO操作析二。所以,我們?cè)谶x擇m大小的時(shí)候节预,要盡量讓每個(gè)節(jié)點(diǎn)的大小等于一個(gè)頁的大小叶摄。讀取一個(gè)節(jié)點(diǎn),只需要一次磁盤IO操作安拟。對(duì)于一個(gè)B+樹來說蛤吓,m值是根據(jù)頁的大小事先計(jì)算好的,也就是說糠赦,每個(gè)節(jié)點(diǎn)最多只能有m個(gè)子節(jié)點(diǎn)柱衔。在往數(shù)據(jù)庫中寫入數(shù)據(jù)的過程中,這樣就有可能使索引中某些節(jié)點(diǎn)的子節(jié)點(diǎn)個(gè)數(shù)超過m愉棱,這個(gè)節(jié)點(diǎn)的大小超過了一個(gè)頁的大小唆铐,讀取這樣一個(gè)節(jié)點(diǎn),就會(huì)導(dǎo)致多次磁盤IO操作奔滑。當(dāng)出現(xiàn)超過m的情況我們就進(jìn)行列表操作艾岂,裂變之后父節(jié)點(diǎn)也會(huì)超過m,同樣的操作我們將父節(jié)點(diǎn)也進(jìn)行一遍朋其。這種級(jí)聯(lián)反應(yīng)會(huì)從下往上王浴,一直影響到根節(jié)點(diǎn)。我們用一個(gè)動(dòng)圖(圖中的m為3)來表示:


image.png

B+樹的特點(diǎn):

  • 每個(gè)節(jié)點(diǎn)中子節(jié)點(diǎn)的個(gè)數(shù)不能超過m梅猿,也不能小于m/2;

  • 根節(jié)點(diǎn)的子節(jié)點(diǎn)個(gè)數(shù)可以不超過m/2氓辣,這是一個(gè)例外;

  • m叉樹只存儲(chǔ)索引,并不真正存儲(chǔ)數(shù)據(jù)袱蚓,這個(gè)有點(diǎn)兒類似跳表; 通過鏈表將葉子節(jié)點(diǎn)串聯(lián)在一起钞啸,這樣可以方便按區(qū)間查找;

  • 一般情況,根節(jié)點(diǎn)會(huì)被存儲(chǔ)在內(nèi)存中喇潘,其他節(jié)點(diǎn)存儲(chǔ)在磁盤中体斩。

“N叉樹”的N值在MySQL中是可以被人工調(diào)整的么?

5.6以后可以通過page大小來間接控制。

最左前綴原則

B+樹這種索引結(jié)構(gòu)颖低,可以利用索引的最左前綴絮吵,來定位記錄。假設(shè)我們有張表里面用name忱屑、age建立了聯(lián)合索引蹬敲,索引示意圖為

image.png

當(dāng)你的邏輯需求是查到所有名字是“張三”的人時(shí)暇昂,可以快速定位到ID4,然后向后遍歷得到所有需要的結(jié)果伴嗡。 如果你要查的是所有名字第一個(gè)字是“張”的人急波,你的SQL語句的條件是"where name like ‘張%’"。這時(shí)闹究,你也能夠用上這個(gè)索引,查找到第一個(gè)符合條件的記錄是ID3食店,然后向后遍歷渣淤,直到不滿足條件為止。 可以看到吉嫩,不只是索引的全部定義价认,只要滿足最左前綴,就可以利用索引來加速檢索自娩。這個(gè)最左前綴可以是聯(lián)合索引的最左N個(gè)字段用踩,也可以是字符串索引的最左M個(gè)字符,在建立聯(lián)合索引的時(shí)候忙迁,如何安排索引內(nèi)的字段順序脐彩。如果通過調(diào)整順序,可以少維護(hù)一個(gè)索引姊扔,那么這個(gè)順序往往就是需要優(yōu)先考慮采用 的惠奸。

索引下推

以市?表的聯(lián)合索引(name, age)為例,如果現(xiàn)在有一個(gè)需求:檢索出表中“名字第一個(gè)字是張恰梢,而且年齡是10歲 的所有男孩”佛南。那么,SQL語句是這么寫的:

select * from tuser where name like '張%' and age=10 and ismale=1;

在MySQL 5.6之前嵌言,只能從ID3開始一個(gè)個(gè)回表嗅回。到主鍵索引上找出數(shù)據(jù)行,再對(duì)比字段值摧茴。而MySQL 5.6 引入的索引下推優(yōu)化(index condition pushdown)绵载, 可以在索引遍歷過程中,對(duì)索引中包含的字段先做判斷苛白, 直接過濾掉不滿足條件的記錄尘分,減少回表次數(shù)。沒有索引下推的情況為:


image.png

5.6以后有索引下推的情況


image.png

問題:

CREATE TABLE `test` ( `a` int(11) NOT NULL, `b` int(11) NOT NULL, `c` int(11) NOT NULL, `d` int(11) NOT NULL, PRIMARY KEY (`a`,`b`), KEY `c` (`c`),
KEY `ca` (`c`,`a`),
KEY `cb` (`c`,`b`) ) ENGINE=InnoDB;

既然主鍵包含了a丸氛、b這兩個(gè)字段培愁,那意味著單獨(dú)在字段c上創(chuàng)建一個(gè)索引,就已經(jīng)包含了三個(gè)字段了呀缓窜,為什么要?jiǎng)?chuàng)建“ca”“cb”這兩個(gè)索引?

如果c列上重復(fù)率很低的情況下,兩個(gè)索引都可以不用建定续。因?yàn)槿绻^濾只剩下幾條數(shù)據(jù),排序也不影響,如果C列重復(fù)度比較高,就需要建立(c,b)的聯(lián)合索引了,來消除排序了谍咆。因?yàn)樵跀?shù)據(jù)量大的情況下,排序是一個(gè)非常耗時(shí)的操作,很有可能還需要磁盤臨時(shí)表來做排序。而且如果沒有(c,b)聯(lián)合索引,limit 1僅僅表示返回給客戶端一條數(shù)據(jù),沒有起到限制掃描行,數(shù)的作用ca列上的索引,由于滿足最左前綴,不用加私股。因?yàn)閏是固定值,那么a列就是有序的.那么這里limit 1就很好限制了只用精準(zhǔn)掃描一條數(shù)據(jù).所以有時(shí)候如果在where條件建立索引的效率差的情況下,在order by limit這一列建索引也是很好的方案,排好序,在回表,只要過濾出滿足條件的limit行,就能及時(shí)停止掃描

總結(jié):

  1. 覆蓋索引:如果查詢條件使用的是普通索引(或是聯(lián)合索引的最左原則字段)摹察,查詢結(jié)果是聯(lián)合索引的字段或是主鍵,不 用回表操作倡鲸,直接返回結(jié)果供嚎,減少IO磁盤讀寫讀取正行數(shù)據(jù)
  2. 最左前綴:聯(lián)合索引的最左 N 個(gè)字段,也可以是字符串索引的最左 M 個(gè)字符
  3. 聯(lián)合索引:根據(jù)創(chuàng)建聯(lián)合索引的順序峭状,以最左原則進(jìn)行where檢索克滴,比如(age,name)以age=1 或 age= 1 and name=‘張 三’可以使用索引优床,單以name=‘張三’ 不會(huì)使用索引劝赔,考慮到存儲(chǔ)空間的問題,還請(qǐng)根據(jù)業(yè)務(wù)需求胆敞,將查找頻繁的數(shù)據(jù)進(jìn)行靠左 創(chuàng)建索引着帽。
  4. 索引下推:like 'hello%’and age >10 檢索,MySQL5.6版本之前移层,會(huì)對(duì)匹配的數(shù)據(jù)進(jìn)行回表查詢仍翰。5.6版本后,會(huì)先過濾掉a ge<10的數(shù)據(jù)观话,再進(jìn)行回表查詢歉备,減少回表率,提升檢索速度
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匪燕,一起剝皮案震驚了整個(gè)濱河市蕾羊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帽驯,老刑警劉巖龟再,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異尼变,居然都是意外死亡利凑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門嫌术,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哀澈,“玉大人,你說我怎么就攤上這事度气「畎矗” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵磷籍,是天一觀的道長适荣。 經(jīng)常有香客問我现柠,道長,這世上最難降的妖魔是什么弛矛? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任够吩,我火速辦了婚禮,結(jié)果婚禮上丈氓,老公的妹妹穿的比我還像新娘周循。我一直安慰自己,他們只是感情好万俗,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布湾笛。 她就那樣靜靜地躺著,像睡著了一般该编。 火紅的嫁衣襯著肌膚如雪迄本。 梳的紋絲不亂的頭發(fā)上硕淑,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天课竣,我揣著相機(jī)與錄音,去河邊找鬼置媳。 笑死于樟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拇囊。 我是一名探鬼主播迂曲,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼寥袭!你這毒婦竟也來了路捧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤传黄,失蹤者是張志新(化名)和其女友劉穎杰扫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膘掰,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡章姓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了识埋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凡伊。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖窒舟,靈堂內(nèi)的尸體忽然破棺而出系忙,到底是詐尸還是另有隱情,我是刑警寧澤惠豺,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布笨觅,位于F島的核電站拦耐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏见剩。R本人自食惡果不足惜杀糯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望苍苞。 院中可真熱鬧固翰,春花似錦、人聲如沸羹呵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冈欢。三九已至歉铝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凑耻,已是汗流浹背太示。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留香浩,地道東北人类缤。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像邻吭,于是被迫代替她去往敵國和親餐弱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359