mysql 字典索引_【大白话mysql】你真的了解 mysql 索引吗?
本文來源于公眾號: 跬步匠心
什么是索引?
當(dāng)我們使用漢語字典查找某個字時,我們會先通過拼音目錄查到那個字所在的頁碼,然后直接翻到字典的那一頁,找到我們要查的字,通過拼音目錄查找比我們拿起字典從頭一頁一頁翻找要快的多,數(shù)據(jù)庫索引也一樣,索引就像書的目錄,通過索引能極大提高數(shù)據(jù)查詢的效率。
索引的實(shí)現(xiàn)方式
在數(shù)據(jù)庫中,常見的索引實(shí)現(xiàn)方式有哈希表、有序數(shù)組、搜索樹。
哈希表
哈希表是通過鍵值對(key-value)存儲數(shù)據(jù)的索引實(shí)現(xiàn)方式,可以將哈希表想象成是一個數(shù)組,將索引通過哈希函數(shù)計算得到該行數(shù)據(jù)在數(shù)組中的位置,然后將數(shù)據(jù)存到數(shù)組中,容易發(fā)現(xiàn)一個問題,如果兩個索引通過哈希函數(shù)計算后得到的數(shù)組位置相同要怎么辦?我們可以采用哈希鏈表,數(shù)組的每個 value 都是一個鏈表,新數(shù)據(jù)直接添加到鏈表尾部。
所以數(shù)據(jù)庫查詢過程為:索引通過哈希函數(shù)計算數(shù)據(jù)所在位置 --> 遍歷指定位置的鏈表,找到滿足條件的數(shù)據(jù)。
每次有新數(shù)據(jù)加入時,新數(shù)據(jù)時直接添加到鏈表尾部,所以添加數(shù)據(jù)時很方便。
哈希表不擅長進(jìn)行區(qū)間查詢,一般都用于等值查詢,因為兩個相鄰索引通過 hash 函數(shù)后計算得到的數(shù)組位置不一定還保持相鄰,需要哈希多次才能把區(qū)間的數(shù)據(jù)全查出來。
有序數(shù)組
顧名思義,有序數(shù)組是按索引大小將數(shù)據(jù)保存在一個數(shù)組上,因為該數(shù)組是有序的,可以通過二分法很容易查到位置,找到第一個位置后,通過向左或者向右遍歷很容易得到所求區(qū)間的數(shù)據(jù)。因此,無論是等值查詢還是區(qū)間查詢,效率都極高。
但缺陷也是顯而易見的,當(dāng)向數(shù)組中間 n 位置插入一條數(shù)據(jù)時,需將 n 后面的數(shù)據(jù)全部往后移動,所以,這種索引一般用于靜態(tài)存儲引擎。
搜索樹
二叉搜索樹:一棵空樹,或者是具有下列性質(zhì)的二叉樹:若它的左子樹不空,則左子樹上所有結(jié)點(diǎn)的值均小于它的根結(jié)點(diǎn)的值;若它的右子樹不空,則右子樹上所有結(jié)點(diǎn)的值均大于它的根結(jié)點(diǎn)的值;二叉搜索樹的左、右子樹也分別為二叉搜索樹。
平衡二叉樹:平衡二叉樹是在二叉搜索樹的基礎(chǔ)上引入的,指的是結(jié)點(diǎn)的左子樹和右子樹的深度差不超過 1。
多叉樹:每個結(jié)點(diǎn)可以有多個子結(jié)點(diǎn),子節(jié)點(diǎn)的大小從左到右依次遞增。
數(shù)據(jù)庫一般使用平衡樹來當(dāng)索引的存儲數(shù)據(jù)結(jié)構(gòu),當(dāng)使用平衡二叉實(shí)現(xiàn)索引時,結(jié)構(gòu)如下圖。
從圖中可發(fā)現(xiàn),每次查詢最多需要訪問 4 個節(jié)點(diǎn)必能得到所要數(shù)據(jù)。例如查詢 user2 時,查詢過程為:userA-->userC-->userF-->user2。所以查詢速度很高,復(fù)雜度為 O (log (n));平衡二叉樹的更新復(fù)雜度也為 O (log (n))。
區(qū)間查詢時,由于搜索樹的特性(左子樹小于右子樹),可以很快的排除掉不滿足條件的節(jié)點(diǎn),查起來速度也是很快的。
思考下為什么用平衡搜索樹呢?
因為普通的二叉樹可能因為插入的數(shù)據(jù)最后變成一個很長的鏈表,查詢復(fù)雜度退化成 O (n)。
如果搜索樹存于內(nèi)存中,與多叉樹相比,二叉樹的搜索速率是最高的,但實(shí)際上數(shù)據(jù)庫使用的是 n 叉樹而不是二叉樹。
索引不僅存于內(nèi)存,還是寫到磁盤上,搜索樹上的每個結(jié)點(diǎn)在磁盤上表現(xiàn)為一個數(shù)據(jù)塊。
多叉樹每個結(jié)點(diǎn)下可以有多個子節(jié)點(diǎn),所以存儲相同數(shù)據(jù)量時多叉樹的樹高比二叉樹小,查詢一個數(shù)據(jù)需要訪問的結(jié)點(diǎn)數(shù)更少,即查詢過程訪問更少的數(shù)據(jù)塊。查詢速度較高。
在 mysql 的 innodb 引擎中,使用 B + 樹來存儲數(shù)據(jù),B + 樹是一種多叉平衡查找樹。
innodb 的索引模型
在 B + 樹中,我們將節(jié)點(diǎn)分為葉子結(jié)點(diǎn)和非葉子結(jié)點(diǎn),非葉子結(jié)點(diǎn)上保存的是索引,而且一個節(jié)點(diǎn)可以保存多個索引;數(shù)據(jù)全部存于葉子結(jié)點(diǎn)上,并且葉子結(jié)點(diǎn)之間通過指針連接起來。
根據(jù)葉子結(jié)點(diǎn)的內(nèi)容不同,innodb 索引分為主鍵索引和非主鍵索引。非主鍵索引也稱為二級索引。主鍵索引的葉子結(jié)點(diǎn)中保存的數(shù)據(jù)為整行數(shù)據(jù),而非主鍵索引葉子節(jié)點(diǎn)保存的是主鍵的值。
通過主鍵索引查詢數(shù)據(jù)時,我們只需查找主鍵索引樹便可以獲取數(shù)據(jù);通過非主鍵索引查詢數(shù)據(jù)時,我們先通過非主鍵索引樹查找到主鍵值,然后再在主鍵索引樹搜索一次,這個過程稱為回表,也就是說非主鍵索引查詢會比主鍵查詢多搜索一棵樹。所以我們應(yīng)盡可能使用主鍵查詢。
B + 樹是一顆 N 叉樹,N 是由什么決定的?能否調(diào)整?
通過修改 page 的大小來間接調(diào)整 N 的大小。一個節(jié)點(diǎn)上的所有數(shù)據(jù)都在一個 page 中,頁越大,每頁存放的索引就越多,N 就越大。數(shù)據(jù)頁調(diào)整后,如果數(shù)據(jù)頁太小層數(shù)會太深,數(shù)據(jù)頁太大,加載到內(nèi)存的時間和單個數(shù)據(jù)頁查詢時間會提高,需要達(dá)到平衡才行。
修改索引的大小。每個索引包括固定字節(jié)數(shù)的 Point 指針和索引字段內(nèi)容,索引字段越小,每頁能存的索引就越多,N 就越大。
索引維護(hù)
添加新行時,將會在索引表上添加一條記錄,如果是索引遞增插入時,數(shù)據(jù)都是追加在當(dāng)前最大索引之后,不會對樹中其他數(shù)據(jù)造成影響;如果新加入的數(shù)據(jù)的索引值位于節(jié)點(diǎn)的中間,需要挪動部分節(jié)點(diǎn)的位置,從而保持索引樹的有序性。
而且,相鄰多個節(jié)點(diǎn)是存儲在同一個數(shù)據(jù)頁上的,此時,如果是在已經(jīng)存儲滿狀態(tài)的數(shù)據(jù)頁中插入節(jié)點(diǎn),會申請新的數(shù)據(jù)頁,將部分?jǐn)?shù)據(jù)挪動到新的數(shù)據(jù)頁,這個過程稱為頁分裂,頁分裂除了會影響性能,還會降低磁盤空間利用率。不規(guī)則數(shù)據(jù)插入時,會造成頻繁的頁分裂。所以,一般情況下會采用遞增主鍵,使新數(shù)據(jù)遞增插入。
當(dāng)相鄰兩個頁由于刪除了數(shù)據(jù),利用率很低之后,會將數(shù)據(jù)頁做合并。
什么情況下應(yīng)該使用業(yè)務(wù)邏輯字段做主鍵?有什么優(yōu)缺點(diǎn)?
業(yè)務(wù)邏輯字段不容易保證索引樹結(jié)點(diǎn)有序插入,這樣寫入成本較高。
innodb 默認(rèn)使用整數(shù)類型作為主鍵,主鍵長度較小,二級索引的葉子結(jié)點(diǎn)中保存的是主鍵值,主鍵長度越小,二級索引的葉子結(jié)點(diǎn)占用空間也就越小。
當(dāng)然,使用業(yè)務(wù)邏輯字段做主鍵也有好處,可以避免回表,每次只需掃描一次主鍵索引樹即可。
綜上,從性能和存儲空間方面考量,自增主鍵往往是更合理的選擇,但是當(dāng)業(yè)務(wù)場景有且只有一個索引,而且該索引為唯一索引時,此時更適合使用業(yè)務(wù)邏輯字段作為主鍵,一個是避免回表,還有一個是只有一個索引也不需要考慮二級索引的空間占用情況了。
索引重建
因為數(shù)據(jù)修改、刪除、頁分裂等原因,會導(dǎo)致數(shù)據(jù)頁空間利用率降低,此時,可以考慮重建索引,將數(shù)據(jù)按順序插入,提高磁盤空間利用率。
重建普通索引時,直接先刪除索引,再重新創(chuàng)建即可。
alter table T drop index k;
alter table T add index(k);
復(fù)制代碼
主鍵索引不能通過上面的語句去重建,因為刪除主鍵索引后,innodb 會如下處理:
如果存在非空且字段類型為數(shù)值的唯一索引(INT and NOT NULL and UNIQUE INDEX), 會將第一個滿足條件的索引作為主鍵索引 , _rowid 為對應(yīng)主鍵,值與唯一索引相同。(可通過 select _rowid from table 查詢)。
如果找不到合適的索引,那么 InnoDB 會自動生成一個不可見的名為 ROW_ID 的列名為 GEN_CLUST_INDEX 的主鍵索引,該列是一個 6 字節(jié)的自增數(shù)值,隨著插入而自增。
所以刪除主鍵索引的結(jié)果其實(shí)是修改了主鍵字段,而普通索引的葉子節(jié)點(diǎn)存的是主鍵的值,所以,一旦修改了主鍵字段,普通索引也會有影響,葉子節(jié)點(diǎn)的值將被修改成新的主鍵字段。
當(dāng)主鍵索引需要重建時,更好的做法是直接使用 alter table t engine=innodb 重建表。
寫在最后
喜歡本文的朋友,歡迎關(guān)注公眾號「跬步匠心」,專注大白話分享實(shí)用技術(shù)
總結(jié)
以上是生活随笔為你收集整理的mysql 字典索引_【大白话mysql】你真的了解 mysql 索引吗?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android tv字体,best登陆「
- 下一篇: mysql.ini环境配置_MySQL配