自助免費(fèi)建站系統(tǒng)電池優(yōu)化大師下載
文章目錄
- 1. 概述
- 2. 常見索引結(jié)構(gòu)
- 2.1 聚簇索引
- 2.2 二級(jí)索引(輔助索引、非聚簇索引)
- 2.3 聯(lián)合索引
- 3. InnoDB的B+樹索引的注意事項(xiàng)
- 3.1 根頁面位置萬年不動(dòng)
- 3.2 內(nèi)節(jié)點(diǎn)中目錄項(xiàng)記錄的唯一性
- 4. MyISAM中的索引方案
- 5. InnoDB和MyISAM對(duì)比
- 6. 小結(jié)
- 7. 補(bǔ)充:MySQL數(shù)據(jù)結(jié)構(gòu)的合理性
- 7.1 全表遍歷
- 7.2 Hash結(jié)構(gòu)
- 7.3 二叉搜索樹
- 7.4 AVL樹
- 7.5 B-Tree
- 7.6 B+Tree
- 7.7 R樹
- 8. 思考題
- 8.1 思考B+樹的存儲(chǔ)能力如何?為何說一般查找記錄,最多只需1~3次磁盤IO?
- 8.2 為什么說B+樹比B-樹更適合實(shí)際應(yīng)用中操作系統(tǒng)的文件索引和數(shù)據(jù)庫索引?
- 9. 小結(jié)
1. 概述
索引是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),相當(dāng)于書籍的目錄。如果表很大有上千萬條數(shù)據(jù),就意味著要做 很多次磁盤I/O
才能找到需要的數(shù)據(jù)。建立索引后若根據(jù)索引(innodb存儲(chǔ)引擎使用的是B+樹結(jié)構(gòu))查詢,相比按順序查找,將極大 減少磁盤I/0的次數(shù)
,加快查找速度;降低 數(shù)據(jù)庫的IO成本
,這也是創(chuàng)建索引最主要的原因
索引是在存儲(chǔ)引擎中實(shí)現(xiàn)的,因此每種存儲(chǔ)引擎的索引不一定完全相同,并且每種存儲(chǔ)引擎不一定支持所有索引類型。同時(shí),存儲(chǔ)引擎可以定義每個(gè)表的 最大索引數(shù)
和 最大索引長度
。所有存儲(chǔ)引擎支持每個(gè)表至少16個(gè)索引,總索引長度至少為256字節(jié)。
缺點(diǎn):
- 創(chuàng)建索引和維護(hù)索引要
耗費(fèi)時(shí)間
,并且隨著數(shù)據(jù)量的增加,所耗費(fèi)的時(shí)間也會(huì)增加 - 索引需要占
磁盤空間
- 索引會(huì)
降低更新表的速度
。當(dāng)對(duì)表中的數(shù)據(jù)進(jìn)行增刪改操作的時(shí)候,索引也要?jiǎng)討B(tài)地維護(hù),這降低了數(shù)據(jù)的維護(hù)速度
因此,選擇使用索引時(shí),需要綜合考慮索引的優(yōu)點(diǎn)和缺點(diǎn)。
索引可以提高查詢的速度,但是會(huì)影響插入記錄的速度。面臨頻繁增刪改情況下,最好的辦法是先刪除表中的索引,然后插入數(shù)據(jù),插入完成后再創(chuàng)建索引。
2. 常見索引結(jié)構(gòu)
首先需要知道,MySQL數(shù)據(jù)庫從磁盤取數(shù)據(jù)到內(nèi)存中是以最小單位數(shù)據(jù)頁來取的,在前面章節(jié)架構(gòu)篇的緩沖池中有提到(默認(rèn)數(shù)據(jù)頁大小是16KB)。
2.1 聚簇索引
特點(diǎn):
-
使用記錄主鍵值的大小進(jìn)行記錄和頁的排序,這包括三個(gè)方面的含義
頁內(nèi)
的記錄是按照主鍵的大小順序排成一個(gè)單向鏈表
- 各個(gè)存放
用戶記錄的頁
也是根據(jù)頁中用戶記錄的主鍵大小順序排成一個(gè)雙向鏈表
- 存放
目錄項(xiàng)記錄的頁
分為不同的層次,在同一層次中的頁也是根據(jù)頁中目錄項(xiàng)記錄的主鍵大小順序排成一個(gè)雙向鏈表
-
B+樹的
葉子節(jié)點(diǎn)
存儲(chǔ)的是完整的用戶記錄所謂完整的用戶記錄,就是指這個(gè)記錄中存儲(chǔ)了所有列的值 (包括隱藏列)。
我們把具有這兩種特性的B+樹稱為 聚簇索引
,所有完整的用戶記錄都存放在這個(gè) 聚簇索引
的葉子節(jié)點(diǎn)處。這種聚簇索引并不需要我們在MySQL語句中顯式的使用INDEX 語句去創(chuàng)建,InnoDB
存儲(chǔ)引擎會(huì) 自動(dòng)
的為我們創(chuàng)建聚族索引(索引即數(shù)據(jù)
)。
缺點(diǎn):
- 插入速度嚴(yán)重依賴于插入順序,按照主鍵的順序插入是最快的方式,否則將會(huì)出現(xiàn)頁分裂,嚴(yán)重影響性能。因此,對(duì)于InnoDB表,一般會(huì)定義一個(gè)自增的ID列為主鍵
- 更新主鍵的代價(jià)很高,因?yàn)閷?huì)導(dǎo)致被更新的行移動(dòng)。因此,對(duì)于innoDB表,我們一般定義主鍵為不可更新
- 二級(jí)索引訪問需要兩次索引查找 ,第一次找到主鍵值,第二次根據(jù)主鍵值找到行數(shù)據(jù)
限制:
- MySQL數(shù)據(jù)庫目前只有InnoDB數(shù)據(jù)引擎支持聚簇索引,而MylSAM并不支持聚簇索引
- 由于數(shù)據(jù)物理存儲(chǔ)排序方式只能有一種,所以每個(gè)MySQL的
表只能有一個(gè)聚簇索引
。一般情況下就是該表的主鍵 - 如果沒有定義主鍵,lnnodb會(huì)選擇
非空的唯一索引
代替。如果沒有這樣的索引,lnnodb會(huì)隱式的定義一個(gè)主鍵來作為聚簇索引 - 為了充分利用聚簇索引的聚簇的特性,所以innodb表的主鍵列盡量 選用有序的順序id,而不建議用無序的id比如UUID、MD5、HASH、字符串列作為主鍵無法保證數(shù)據(jù)的順序增長
2.2 二級(jí)索引(輔助索引、非聚簇索引)
上邊介紹的 聚簇索引
只能在搜索條件是 主鍵值
時(shí)才能發(fā)揮作用,因?yàn)锽+樹中的數(shù)據(jù)都是按照主鍵進(jìn)行排序的。那如果我們想以別的列作為搜索條件該怎么辦呢?肯定不能是從頭到尾沿著鏈表依次遍歷記錄一遍。
答案:我們可以 多建幾棵B+樹
,不同的B+樹中的數(shù)據(jù)采用不同的排序規(guī)則。比方說我們用 c2 列的大小作為數(shù)據(jù)頁、頁中記錄的排序規(guī)則,再建一棵B+樹,效果如下圖所示:
這個(gè)B+樹與上面聚簇索引有一個(gè)主要的不同點(diǎn):
- B+樹的葉子節(jié)點(diǎn)存儲(chǔ)的并不是完整的用戶記錄,而是
c2列+主鍵
這兩個(gè)列的值
二級(jí)索引查找流程:
當(dāng)根據(jù)二級(jí)索引查找時(shí),先根據(jù)二級(jí)索引找到主鍵值,再用主鍵值去聚簇索引中通過主鍵值查找數(shù)據(jù)(這個(gè)過程便是回表);如果索引中便包含了所要查找的數(shù)據(jù)列,如c2,則不需要進(jìn)行回表,可直接取出數(shù)據(jù),此時(shí)便稱該索引為覆蓋索引
c2值相同時(shí)還會(huì)有別的處理,具體可等后面看3.2。
2.3 聯(lián)合索引
我們也可以同時(shí)以多個(gè)列的大小作為排序規(guī)則,也就是同時(shí)為多個(gè)列建立索引,比方說我們想讓B+樹按照 c2和c3列
的大小進(jìn)行排序,這個(gè)包含兩層含義:
- 先把各個(gè)記錄和頁按照c2列進(jìn)行排序
- 在記錄的c2列相同的情況下,采用c3列進(jìn)行排序
為c2和c3列建立的索引的示意圖如下:
如圖所示,我們需要注意以下幾點(diǎn):
- 每條 目錄項(xiàng)記錄 都由 c2、c3、頁號(hào) 這三個(gè)部分組成,各條記錄先按照c2列的值進(jìn)行排序,如果記錄的c2列相同,則按照c3列的值進(jìn)行排序
- B+樹
葉子節(jié)點(diǎn)
處的用戶記錄由 c2、c3和主鍵c1列 組成
以c2和c3列的大小為排序規(guī)則建立的B+樹稱為 聯(lián)合索引,本質(zhì)上也是一個(gè)二級(jí)索引。
3. InnoDB的B+樹索引的注意事項(xiàng)
3.1 根頁面位置萬年不動(dòng)
B+樹的形成過程:
- 每當(dāng)為某個(gè)表創(chuàng)建一個(gè)B+樹索引(聚族索引不是人為創(chuàng)建的,默認(rèn)就有)的時(shí)候,都會(huì)為這個(gè)索引創(chuàng)建一個(gè)
根節(jié)點(diǎn)
頁面。最開始表中沒有數(shù)據(jù)的時(shí)候,每個(gè)B+樹索引對(duì)應(yīng)的根節(jié)點(diǎn)
中既沒有用戶記錄,也沒有目錄項(xiàng)記錄 - 隨后向表中插入用戶記錄時(shí),先把用戶記錄存儲(chǔ)到這個(gè)
根節(jié)點(diǎn)
中 - 當(dāng)根節(jié)點(diǎn)中的可用
空間用完時(shí)
繼續(xù)插入記錄,此時(shí)會(huì)將根節(jié)點(diǎn)中的所有記錄復(fù)制到一個(gè)新分配的頁,比如頁a
中,然后對(duì)這個(gè)新頁進(jìn)行頁分裂
的操作,得到另一個(gè)新頁,比如頁b
。這時(shí)新插入的記錄根據(jù)鍵值(也就是聚簇索引中的主鍵值,二級(jí)索引中對(duì)應(yīng)的索引列的值)的大小就會(huì)被分配到頁a
或者頁b
中,而根節(jié)點(diǎn)
便升級(jí)為存儲(chǔ)目錄項(xiàng)記錄的頁
這個(gè)過程特別注意的是:一個(gè)B+樹索引的根節(jié)點(diǎn)自誕生之日起,便不會(huì)再移動(dòng)。這樣只要我們對(duì)某個(gè)表建立一個(gè)索引,那么它的根節(jié)點(diǎn)的頁號(hào)便會(huì)被記錄到某個(gè)地方,然后凡是 InnoDB
存儲(chǔ)引擎需要用到這個(gè)索引的時(shí)候,都會(huì)從那個(gè)固定的地方取出根節(jié)點(diǎn)的頁號(hào),從而來訪問這個(gè)索引
3.2 內(nèi)節(jié)點(diǎn)中目錄項(xiàng)記錄的唯一性
在上述二級(jí)索引中,如果第二層目錄項(xiàng)存在相同值,此時(shí)需要再插入一個(gè)相同值時(shí),將無法得知將要插入的值應(yīng)該插入到哪個(gè)葉子節(jié)點(diǎn)頁面中去,故實(shí)際上二級(jí)索引為保持內(nèi)節(jié)點(diǎn)的唯一性,還會(huì)再目錄項(xiàng)中也加上主鍵值,這樣就可以在目錄項(xiàng)c2相同的情況下,再比較主鍵值即可得到葉子節(jié)點(diǎn)應(yīng)該插入到哪了,如下圖所示:
這種情況下,我們需要插入(9, 1, ‘x’)時(shí)就知道,我們需要插入到頁5中。
4. MyISAM中的索引方案
索引/存儲(chǔ)引擎 | MyISAM | InnoDB | Memory |
---|---|---|---|
B+樹索引 | 支持 | 支持 | 支持 |
即使多個(gè)存儲(chǔ)引擎支持同一種類型的索引,但是他們的實(shí)現(xiàn)原理也是不同的。Innodb和MyISAM默認(rèn)的索引是B+樹索引;而Memory默認(rèn)的索引是Hash索引。
MyISAM引擎使用 B+Tree
作為索引結(jié)構(gòu),葉子節(jié)點(diǎn)的data域存放的是 數(shù)據(jù)記錄的地址
。
MyISAM 的索引方案雖然也使用樹形結(jié)構(gòu),但是卻 將索引和數(shù)據(jù)分開存儲(chǔ)
:
- 將表中的記錄
按照記錄的插入順序
單獨(dú)存儲(chǔ)在一個(gè)文件中,稱之為數(shù)據(jù)文件
。這個(gè)文件并不劃分為若干個(gè)數(shù)據(jù)頁,有多少記錄就往這個(gè)文件中塞多少記錄就成了。由于在插入數(shù)據(jù)的時(shí)候并沒有刻意按照主鍵大小排序
,所以我們并不能在這些數(shù)據(jù)上使用二分法進(jìn)行查找 - 使用MyISAM存儲(chǔ)引擎的表會(huì)把索引信息另外存儲(chǔ)到一個(gè)稱為
索引文件
的另一個(gè)文件中。MyISAM 會(huì)單獨(dú)為表的主鍵創(chuàng)建一個(gè)索引,只不過在索引的葉子節(jié)點(diǎn)中存儲(chǔ)的不是完整的用戶記錄,而是主鍵值 + 數(shù)據(jù)記錄地址
的組合
這里設(shè)表一共有三列,假設(shè)我們以Col1為主鍵,上圖是一個(gè)MyISAM表的主索引 (Primary key) 示意??梢钥闯鯩yISAM的索引文件僅僅保存數(shù)據(jù)記錄的地址。在MyISAM中,主鍵索引和二級(jí)索引(Secondary key) 在結(jié)構(gòu)上沒有任何區(qū)別,只是主鍵索引要求key是唯一的,而非主鍵索引的key可以重復(fù),故MyISAM的主鍵及非主鍵索引均為二級(jí)索引。
因此,MylSAM中索引檢索的算法為: 首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然后以data域的值為地址,讀取相應(yīng)數(shù)據(jù)記錄。
5. InnoDB和MyISAM對(duì)比
MyISAM的索引方式都是“非聚簇”的,與InnoDB包含1個(gè)聚簇索引是不同的,兩種引擎中索引的區(qū)別:
- 在InnoDB存儲(chǔ)引擎中,我們只需要根據(jù)主鍵值對(duì)
聚簇索引
進(jìn)行一次查找就能找到對(duì)應(yīng)的記錄,而在 MyISAM 中卻需要進(jìn)行一次回表
操作,意味著MyISAM中建立的索引相當(dāng)于全部都是二級(jí)索引
- lnnoDB的數(shù)據(jù)文件本身就是索引文件,而MyISAM索引文件和數(shù)據(jù)文件是
分離的
,索引文件僅保存數(shù)據(jù)記錄的地址 - lnnoDB的非聚簇索引data域存儲(chǔ)相應(yīng)記錄
主鍵的值
,而MylSAM索引記錄的是地址。換句話說,InnoDB的所有非聚簇索引都引用主鍵作為data域 - MyISAM的回表操作是十分
快速
的,因?yàn)槭悄弥刂菲屏恐苯拥轿募腥?shù)據(jù)的,反觀InnoDB是通過獲取主鍵之后再去聚簇索引里找記錄,雖然說也不慢,但還是比不上直接用地址去訪問 - lnnoDB要求表
必須有主鍵( MyISAM可以沒有)
。如果沒有顯式指定,則MySQL系統(tǒng)會(huì)自動(dòng)選擇一個(gè)可以非空且唯一標(biāo)識(shí)數(shù)據(jù)記錄的列作為主鍵。如果不存在這種列,則MySQL自動(dòng)為InnoDB表生成一個(gè)隱含字段作為主鍵,這個(gè)字段長度為6個(gè)字節(jié),類型為長整型
6. 小結(jié)
- 不建議使用過長的字段作為主鍵,因?yàn)樗卸?jí)索引都引用主鍵索引,過長的主鍵索引會(huì)令二級(jí)索引變得過大
- 用非單調(diào)的字段作為主鍵在innoDB中不是個(gè)好主意,因?yàn)镮nnoDB數(shù)據(jù)文件本身是一棵B+Tree,非單調(diào)的主鍵會(huì)造成在插入新記錄時(shí),數(shù)據(jù)文件為了維持B+Tree的特性而頻繁的分裂調(diào)整,十分低效,而使用
自增字段作為主鍵則是一個(gè)很好的選擇
(分布式系統(tǒng)要考慮主鍵唯一性,有雪花算法等解決方案后續(xù)研究) - 聯(lián)合索引應(yīng)將數(shù)據(jù)重復(fù)少的放前面,使得通過索引能按索引左側(cè)字段盡快找到對(duì)應(yīng)數(shù)據(jù)
- 聯(lián)合索引遵守最左匹配原則,即查找邏輯為先查找索引最左側(cè)字段,并逐個(gè)比對(duì),而無法從索引右邊字段開始
7. 補(bǔ)充:MySQL數(shù)據(jù)結(jié)構(gòu)的合理性
從MySQL的角度講,不得不考慮一個(gè)現(xiàn)實(shí)問題就是磁盤IO。如果我們能讓索引的數(shù)據(jù)結(jié)構(gòu)盡量減少硬盤的 I/0 操作,所消耗的時(shí)間也就越小??梢哉f,磁盤的 I/0 操作次數(shù)
對(duì)索引的使用效率至關(guān)重要
查找都是索引操作,一般來說索引非常大,尤其是關(guān)系型數(shù)據(jù)庫,當(dāng)數(shù)據(jù)量比較大的時(shí)候,索引的大小有可能幾個(gè)G甚至更多,為了減少索引在內(nèi)存的占用,數(shù)據(jù)庫索引是存儲(chǔ)在外部磁盤上的。當(dāng)我們利用索引查詢的時(shí)候不可能把整個(gè)索引全部加載到內(nèi)存,只能 逐一加載
,那么MySQL衡量查詢效率的標(biāo)準(zhǔn)就是磁盤IO次數(shù)
7.1 全表遍歷
需逐個(gè)加載數(shù)據(jù)頁,如果在最后一頁,需遍歷全表,將數(shù)據(jù)頁都加載進(jìn)內(nèi)存,要進(jìn)行大量I/O操作。
7.2 Hash結(jié)構(gòu)
哈希的查詢/插入/修改/刪除的平均時(shí)間復(fù)雜度都是O(1),但索引結(jié)構(gòu)還是采取了樹型,具體原因有:
- Hash 索引僅能滿足(=) (<>)和 IN 查詢。如果進(jìn)行
范圍查詢
,哈希型的索引,時(shí)間復(fù)雜度會(huì)退化為O(n);而樹型的“有序”特性,依然能夠保持O(log2N)的高效率 - Hash 索引還有一個(gè)缺陷,數(shù)據(jù)的存儲(chǔ)是
沒有順序的
,在ORDER BY的情況下,使用 Hash 索引還需要對(duì)數(shù)據(jù)重新排序。 - 對(duì)于聯(lián)合索引的情況,Hash 值是將聯(lián)合索引鍵合并后一起來計(jì)算的,無法對(duì)單獨(dú)的一個(gè)鍵或者幾個(gè)索引鍵進(jìn)行查詢
- 對(duì)于等值查詢來說,通常 Hash 索引的效率更高,不過也存在一種情況,就是
索引列的重復(fù)值如果很多,效率就會(huì)降低
。這是因?yàn)橛龅?Hash 沖突時(shí),需要遍歷桶中的行指針來進(jìn)行比較,找到查詢的關(guān)鍵字,非常耗時(shí)。所以,Hash 索引通常不會(huì)用到重復(fù)值多的列上,比如列為性別、年齡的情況等
另外,InnoDB 本身不支持 Hash 索引,但是提供 自適應(yīng) Hash 索引 (Adaptive Hash index)
。什么情況下才會(huì)使用自適應(yīng) Hash 索引呢?如果某個(gè)數(shù)據(jù)經(jīng)常被訪問,當(dāng)滿足一定條件的時(shí)候,就會(huì)將這個(gè)數(shù)據(jù)頁的地址存放到Hash 表中。這樣下次查詢的時(shí)候,就可以直接找到這個(gè)頁面的所在位置。這樣讓 B+ 樹也具備了 Hash 索引的優(yōu)點(diǎn)。
自適應(yīng) Hash索引變量默認(rèn)是開啟的:
show variables like '%adaptive_hash_index';
7.3 二叉搜索樹
如果利用樹形結(jié)構(gòu)作為索引結(jié)構(gòu),那么磁盤的0次數(shù)和索引樹的高度是相關(guān)的。如果插入數(shù)據(jù)一直是逐漸變大,二叉搜索樹就會(huì)形成一根鏈條,查找數(shù)據(jù)的時(shí)間復(fù)雜度會(huì)退化為O(n),為了提高查詢效率就需要盡量降低樹的高度
,需要把原來“瘦高”的樹結(jié)構(gòu)變的矮胖,樹的每層的分叉越多越好
7.4 AVL樹
為解決二叉樹退化為鏈表的問題,人們提出了平衡二叉搜索樹(Balanced Binary Tree),又稱AVL樹(有別于AVL算法)。這樣搜索的時(shí)間復(fù)雜度就能穩(wěn)定在Log2N,但是當(dāng)數(shù)據(jù)量大時(shí),樹的深度一樣會(huì)很高,而每訪問一次節(jié)點(diǎn)就許可進(jìn)行一次磁盤I/O操作,需要消耗的時(shí)間一樣很多。
7.5 B-Tree
針對(duì)以上問題,如果把二叉樹改為M叉樹(M>2),M叉平衡樹的高度會(huì)遠(yuǎn)小于二叉樹的高度,所以可以讓樹更加“矮胖”。B樹的英文是Balance Tree,也就是多路平衡樹。簡寫為B-Tree(-表示兩個(gè)單詞相連,念橫杠而非B減樹),與B+樹典型區(qū)別是非葉子節(jié)點(diǎn)也會(huì)存儲(chǔ)數(shù)據(jù)。對(duì)于大量的索引數(shù)據(jù)來說,采用B樹的結(jié)構(gòu)是非常合適的,因?yàn)闃涞母叨纫h(yuǎn)小于二叉樹的高度。
7.6 B+Tree
B+樹也是一種多路搜索樹,基于 B 樹
做出了改進(jìn),主流的 DBMS 都支持 B+ 樹的索引方式,比如 MySQL。相比于B-Tree,B+Tree適合文件索引系統(tǒng)
。
B+Tree和B-Tree的根本差異就是B+樹的中間節(jié)點(diǎn)并不直接存儲(chǔ)數(shù)據(jù),這樣的好處是?
- B+樹的查詢效率更穩(wěn)定,因?yàn)橹虚g節(jié)點(diǎn)無需存儲(chǔ)數(shù)據(jù),要查到數(shù)據(jù)必須查到數(shù)據(jù)的葉子節(jié)點(diǎn)
- B+樹的查詢效率更高,因?yàn)橹虚g節(jié)點(diǎn)無需存儲(chǔ)數(shù)據(jù),B+樹通常比B樹
更矮胖
,查詢鎖需要的磁盤I/O也會(huì)更少,同樣的磁盤頁,B+樹可存儲(chǔ)更多的節(jié)點(diǎn)關(guān)鍵字 - B+樹查詢范圍的效率更高,B+樹葉子節(jié)點(diǎn)間有指針,數(shù)據(jù)又是遞增,范圍查找可通過指針連接查找,而B樹中要通過中序遍歷,效率要低很多
B樹和B+樹都可以作為索引的數(shù)據(jù)結(jié)構(gòu),在MySQL中采用的是B+樹,但B樹和B+樹各有自己的應(yīng)用場景,不能完全說B+樹比B樹好
注意:索引樹不會(huì)一次性加載(數(shù)據(jù)量大必然導(dǎo)致索引增大),而是逐一加載每一個(gè)磁盤頁,因?yàn)榇疟P頁對(duì)應(yīng)著索引樹的節(jié)點(diǎn)
7.7 R樹
R-Tree在MySQL很少使用,僅支持 geometry數(shù)據(jù)類型
,支持該類型的存儲(chǔ)引擎只有myisam、bdb、innodb、ndb、archive幾種。舉個(gè)R樹在現(xiàn)實(shí)領(lǐng)域中能夠解決的例子: 查找20英里以內(nèi)所有的餐廳。如果沒有R樹你會(huì)怎么解決?一般情況下我們會(huì)把餐廳的坐標(biāo)(x,y)分為兩個(gè)字段存放在數(shù)據(jù)庫中,一個(gè)字段記錄經(jīng)度,另一個(gè)字段記錄緯度。這樣的話我們就需要遍歷所有的餐廳獲取其位置信息,然后計(jì)算是否滿足要求。如果一個(gè)地區(qū)有100家餐廳的話,我們就要進(jìn)行100次位置計(jì)算操作了,如果應(yīng)用到谷歌、百度地圖這種超大數(shù)據(jù)庫中,這種方法便必定不可行了。R樹就很好的 解決了這種高維空間搜索問題
。它把B樹的思想很好的擴(kuò)展到了多維空間,采用了B樹分割空間的思想,并在添加、刪除操作時(shí)采用合并、分解結(jié)點(diǎn)的方法,保證樹的平衡性。因此,R樹就是一棵用來存儲(chǔ)高維數(shù)據(jù)的平衡樹
。相對(duì)于B-Tree,R-Tree的優(yōu)勢在于范圍查找
8. 思考題
8.1 思考B+樹的存儲(chǔ)能力如何?為何說一般查找記錄,最多只需1~3次磁盤IO?
InnoDB 存儲(chǔ)引擎中頁的大小為 16KB,一般表的主鍵類型為INT(占用4個(gè)字節(jié)或BIGINT(占用8個(gè)字節(jié)),指針類型也一般為4或8個(gè)字節(jié),也就是說一個(gè)頁(B+Tree 中的一個(gè)節(jié)點(diǎn))中大概存儲(chǔ)16KB/(8B+8B)=1K個(gè)鍵值 (因?yàn)槭枪乐?#xff0c;為方便計(jì)算,這里的K 取值為10^3。也就是說一個(gè)深度為3的B+Tree 索引可以維護(hù)10^3 * 10^ 3 * 10^3 = 10 億條記錄。(這里假定一個(gè)數(shù)據(jù)頁也存儲(chǔ)10^3條行記錄數(shù)據(jù)了)
實(shí)際情況中每個(gè)節(jié)點(diǎn)可能不能填充滿,因此在數(shù)據(jù)庫中,B+Tree 的高度一般都在 2~4 層。MySQL的InnoDB 存儲(chǔ)引擎在設(shè)計(jì)時(shí)是將根節(jié)點(diǎn)常駐內(nèi)存的,也就是說查找某一鍵值的行記錄時(shí)最多只需要 1~3次磁盤I/0 操作。
8.2 為什么說B+樹比B-樹更適合實(shí)際應(yīng)用中操作系統(tǒng)的文件索引和數(shù)據(jù)庫索引?
- B+樹的磁盤讀寫代價(jià)更低,B+樹的內(nèi)部結(jié)點(diǎn)并沒有指向關(guān)鍵字具體信息的指針。因此其內(nèi)部結(jié)點(diǎn)相對(duì)B 樹更小。如果把所有同一內(nèi)部結(jié)點(diǎn)的關(guān)鍵字存放在同一盤塊中,那么盤塊所能容納的關(guān)鍵字?jǐn)?shù)量也越多。一次性讀入內(nèi)存中的需要查找的關(guān)鍵字也就越多。相對(duì)來說IO讀寫次數(shù)也就降低了
- B+樹的查詢效率更加穩(wěn)定
9. 小結(jié)
使用索引可以幫助我們從海量的數(shù)據(jù)中快速定位想要查找的數(shù)據(jù),不過索引也存在一些不足,比如占用存儲(chǔ)空間、降低數(shù)據(jù)庫寫操作的性能等,如果有多個(gè)索引還會(huì)增加索引選擇的時(shí)間。當(dāng)我們使用索引時(shí),需要平衡索引的利(提升查詢效率)和弊(維護(hù)索引所需的代價(jià))。
在實(shí)際工作中,我們還需要基于需求和數(shù)據(jù)本身的分布情況來確定是否使用索引,盡管 索引不是萬能的
,但 數(shù)據(jù)量大的時(shí)候不使用索引是不可想象的
,畢竟索引的本質(zhì),是幫助我們提升數(shù)據(jù)檢索的效率