InnoDB索引

1、索引的建立 / 數據的存儲

? 一條條數據存儲到頁中后,各個數據頁組成了一個雙向鏈表,而每個數據頁中的記錄會按照主鍵值從小到大的順序組成一個單向鏈表。此時,如果我想根據主鍵值查詢一條記錄,只能從第一個數據頁開始一個頁一個頁地去查詢,這種全表搜索方式的效率想想也不會很高,因此 MySQL 選擇為每一個存儲了用戶數據的數據頁建立目錄,通過目錄確定目標數據在哪個頁,然后再在目標頁中根據 Page Directory (頁目錄) + 二分法查詢目標數據。這種方式的前提條件就是下一個數據頁中用戶記錄的主鍵值必須大于上一個頁中用戶記錄的主鍵值,即保證數據的有序性,保證二分法的可用。我們稱數據頁中存儲的該種數據為目錄項記錄,稱存儲的用戶自定義內容為用戶項記錄,以此來區分。

? 存儲目錄項記錄、用戶項記錄用的都是數據頁(頁面類型一致),都會為主鍵值生成 Page Directory(頁目錄),從而在按照主鍵值進行查找時可以使用二分法來加快查詢速度。只不過前者存儲的列只有主鍵值(頁中最小主鍵值)和頁號,而后者不僅存有用戶自定義內容,還有 InnoDB 隱藏列;前者記錄頭信息的 record_type = 1(代表目錄項記錄),后者 record_type = 0(代表普通記錄)。因為在這些目錄頁中只存儲了主鍵值和頁號,所以能夠記錄很多數據,但隨著數據的增多,加上頁面大小默認為 16KB,遲早會出現一張表不足以記錄所有的目錄項記錄,那就只能再新增一頁,這些頁也存有上一個和下一個頁的頁號,以此也構成一個雙向鏈表。

? 由此可見,隨著數據的增多,目錄項記錄數據頁也將變成一個雙向鏈表,那我們再根據主鍵查詢時,又回到了原始問題,即需要一頁一頁遍歷這個雙向鏈表來找到目錄項記錄頁。為了解決這個問題,只能再為創建一個更高的目錄,這樣的話,就形成了一個多級目錄,層層嵌套,最終的小目錄(葉子節點)存儲的才是真實數據。

? 在InnoDB存儲引擎中,聚簇索引就是數據的存儲方式(所有的用戶記錄都存儲在了葉子節點),也就是所謂的索引即數據,數據即索引

? 以上過程是從葉子節點到根節點構建B+樹的角度來描述聚簇索引的構建過程,但是實際上B+樹的構建過程是這樣的:

  • 每當為某個表創建一個B+樹索引(聚簇索引不是人為創建的,默認就有)的時候,都會為這個索引創建一個根節點頁面。最開始表中沒有數據的時候,每個B+樹索引對應的根節點中既沒有用戶記錄,也沒有目錄項記錄。
  • 隨后向表中插入用戶記錄時,先把用戶記錄存儲到這個根節點中。
  • 當根節點中的可用空間用完時繼續插入記錄,此時會將根節點中的所有記錄復制到一個新分配的頁,比如頁a中,然后對這個新頁進行頁分裂的操作,得到另一個新頁,比如頁b。這時新插入的記錄根據鍵值(也就是聚簇索引中的主鍵值,二級索引中對應的索引列的值)的大小就會被分配到頁a或者頁b中,而根節點便升級為存儲目錄項記錄的頁。
  • 當頁b存儲空間用完時,則重復第三步進行頁分裂,創建頁c用于存儲新記錄,并將根節點指向頁c。

? 一個B+樹索引的根節點自誕生之日起,便不會再移動。這樣只要我們對某個表建立一個索引,那么它的根節點的頁號便會被記錄到某個地方,然后凡是InnoDB存儲引擎需要用到這個索引的時候,都會從那個固定的地方取出根節點的頁號,從而來訪問這個索引,也就是常說的聚簇索引的頂層常駐內存

2、重點概念:

2.1 頁分裂:

? 假設此時聚簇索引只有一個葉子節點,且存儲空間(16KB)已滿,此時想向表中插入一條記錄,這條記錄的主鍵id在已有主鍵的范圍內,也就是說這條記錄的主鍵并不是最大的,所以需要新增一個數據頁,將舊數據頁中的最大記錄移到新頁中,將新增記錄按主鍵大小順序插入到舊頁中,這個過程就被稱為頁分裂。

2.2 聚簇索引

  • 使用記錄主鍵值的大小進行記錄和頁的排序,這包括三個方面的含義:
    • 頁內的記錄是按照主鍵的大小順序排成一個單向鏈表。
    • 各個存放用戶記錄的頁也是根據頁中用戶記錄的主鍵大小順序排成一個雙向鏈表。
    • 存放目錄項記錄的頁分為不同的層次,在同一層次中的頁也是根據頁中目錄項記錄的主鍵大小順序排成一個雙向鏈表。
  • B+樹的葉子節點存儲的是完整的用戶記錄。
    • 所謂完整的用戶記錄,就是指這個記錄中存儲了所有列的值(包括隱藏列)。
  • 目錄項記錄中是主鍵+頁號

2.3 非聚簇索引

  • 使用記錄c2列的大小進行記錄和頁的排序,這包括三個方面的含義:
    • 頁內的記錄是按照c2列的大小順序排成一個單向鏈表。
    • 各個存放用戶記錄的頁也是根據頁中記錄的c2列大小順序排成一個雙向鏈表。
    • 存放目錄項記錄的頁分為不同的層次,在同一層次中的頁也是根據頁中目錄項記錄的c2列大小順序排成一個雙向鏈表。
  • B+樹的葉子節點存儲的并不是完整的用戶記錄,而只是c2列+主鍵這兩個列的值。
  • 目錄項記錄中不再是主鍵+頁號的搭配,而變成了c2列+頁號的搭配。

2.4 回表:

? 聚簇索引中葉子節點的value部分存儲的是行數據,而非聚簇索引中葉子節點的value部分存儲的是主鍵值。當查詢中使用到了索引 k,就需要先搜索k索引樹找到對應的主鍵值,再根據主鍵值搜索主鍵索引樹,這樣才能獲取到所要查詢的值,這個過程就被稱為回表。

2.5 覆蓋索引:

? 如果執行的語句是select ID from T where k between 3 and 5,這時只需要查ID的值,而ID的值已經在k索引樹上了,因此可以直接提供查詢結果,不需要回表。也就是說,在這個查詢里面,索引k已經“覆蓋了”我們的查詢需求,我們稱為覆蓋索引。

? 由于覆蓋索引可以減少樹的搜索次數(減少回表的次數),顯著提升查詢性能,所以使用覆蓋索引是一個常用的性能優化手段。

2.6 索引下推

3、使用

3.1 使用場景

1、用于條件匹配

如果想要借助聯合索引優化查詢時,必須遵循“最左匹配原則”。

  • 全值匹配
  • 匹配左邊的列
  • 匹配列前綴
  • 匹配范圍值
  • 精確匹配某一列并范圍匹配另外一列

2、用于排序

3、用于分組

3.2 創建索引注意事項

1、只為用于搜索、排序或分組的列創建索引:

? ? 對于經常出現在查詢列表,但不參與 or 很少參與條件篩選的字段無需創建索引。

2、為列的基數大的列創建索引:

? ? 為字段構建索引樹的時,需要對字段進行排序,以基數為 1 的字段舉例,此時字段值都相同,給它排序沒有任何意義(排不排序結果一樣),不能提升查詢效率,反而在增刪改的時候還要對索引樹進行維護,因此最好為基數大的列創建索引,為基數太小列的建立索引效果可能不好。

3、索引列的類型盡量小:

  • 數據類型越小,在查詢時進行的比較操作越快(CPU層次)
  • 數據類型越小,索引占用的存儲空間就越少,在一個數據頁內就可以放下更多的記錄,從而減少磁盤I/O帶來的性能損耗,也就意味著可以把更多的數據頁緩存在內存中,從而加快讀寫效率。

4、可以只對字符串值的前綴建立索引:

? ? 當對長度很長的字符串建立索引時,其索引結構的存儲將會花費較大的空間,其次在進行字符串比較時也會花費較多的時間,此時我們可以做一個居中的選擇,只為字符串的前幾位字符建立索引。

5、只有索引列在比較表達式中單獨出現才可以適用索引:

? ? 索引列加入運算 or 使用函數會造成索引失效。

6、為了盡可能少的讓聚簇索引發生頁面分裂和記錄移位的情況,建議讓主鍵擁有AUTO_INCREMENT屬性:

? ? 一般我們新增記錄時不會指定主鍵是多少,而是采用自增的形式。因為如果指定主鍵進行插入的話,很有可能沒按主鍵從小到大的順序插入,但是為了保證快速查詢,MySQL會對不按大小順序插入的數據進行移位、頁分裂,造成不必要的性能損耗。

7、定位并刪除表中的重復和冗余索引:

? ? 避免出現重復、冗余的索引,以避免對其進行的維護成本。

8、盡量使用覆蓋索引進行查詢,避免回表帶來的性能損耗

4、面試常問

4.1 MySQL索引的底層實現(簡單描述):

? 它實際上是一個B+樹,首先當我們存入數據時,它會基于數據進行一個排序,排序之后,會使用指針以鏈表的形式連接起來,同時mysql在底層為了進一步優化,基于頁的形式進行管理索引,也就是對我們的數據進行了一頁一頁的存儲,默認頁的大小為16KB。站在整個B+樹這個數據結構來講,一個三層的B+樹可存儲進8億-10億左右的數據,所以一般的項目用兩層足矣。站在一個兩層的B+結構來講,如果基于主鍵查詢,最多動用一次磁盤IO,因為它的頂層是常駐內存的。

4.2 索引為什么是 B+樹,而不是 B樹 呢?

參考資料: B樹、B+樹詳解 - Assassinの - 博客園

原因:

??? B-Tree結構圖中可以看到每個節點中不僅包含數據的key值,還有data值。而每一個頁的存儲空間是有限的,如果data數據較大時將會導致每個節點(即一個頁)能存儲的key的數量很小,當存儲的數據量很大時同樣會導致B-Tree的深度較大,增大查詢時的磁盤I/O次數,進而影響查詢效率。在B+Tree中,所有數據記錄節點都是按照鍵值大小順序存放在同一層的葉子節點上,而非葉子節點上只存儲key值信息,這樣可以大大加大每個節點存儲的key值數量,降低B+Tree的高度。

實際情況:

  • B+Tree中每個節點可能不能填充滿,因此在數據庫中,B+Tree的高度一般都在2~4層。
  • mysql的InnoDB存儲引擎在設計時是將根節點常駐內存的,也就是說查找某一鍵值的行記錄時最多只需要1~3次磁盤I/O操作。

總結:B+Tree只有葉子節點可以存儲數據,而B-Tree非葉子節點也必須存儲數據,當存儲的數據量很大時,會導致B-Tree的深度較大,增大查詢時的磁盤I/O次數,進而影響查詢效率。

4.3 使用聚簇索引的優勢:

  • 問題:每次使用輔助索引檢索都要經過兩次B+樹查找,看上去聚簇索引的效率明顯要低于非聚簇索引,這不是多此一舉嗎?聚簇索引的優勢在哪?
  • 1.由于行數據和聚簇索引的葉子節點存儲在一起,同一頁中會有多條行數據,訪問同一數據頁不同行記錄時,已經把頁加載到了Buffer中(緩存器),再次訪問時,會在內存中完成訪問,不必訪問磁盤。這樣主鍵和行數據是一起被載入內存的,找到葉子節點就可以立刻將行數據返回了,如果按照主鍵Id來組織數據,獲得數據更快。
  • 2.輔助索引的葉子節點,存儲主鍵值,而不是數據的存放地址。好處是當行數據放生變化時,索引樹的節點也需要分裂變化;或者是我們需要查找的數據,在上一次IO讀寫的緩存中沒有,需要發生一次新的IO操作時,可以避免對輔助索引的維護工作,只需要維護聚簇索引樹就好了。另一個好處是,因為輔助索引存放的是主鍵值,減少了輔助索引占用的存儲空間大小。

?

4.4 聚簇索引需要注意什么?

  • 當使用主鍵為聚簇索引時,主鍵最好不要使用uuid,因為uuid的值太過離散,不適合排序且可能出線新增加記錄的uuid,會插入在索引樹中間的位置,導致索引樹調整復雜度變大,消耗更多的時間和資源。
  • 建議使用int類型的自增,方便排序并且默認會在索引樹的末尾增加主鍵值,對索引樹的結構影響最小。而且,主鍵值占用的存儲空間越大,輔助索引中保存的主鍵值也會跟著變大,占用存儲空間,也會影響到IO操作讀取到的數據量。

?

4.5 為什么要設置主鍵自增?

  • 保證每一行數據都有一個唯一標識符,使數據具有唯一性
  • 設置主鍵自增可以使索引建立時避免數據的重排(數據無序時需要排序,使數據按從小到大的順序排列),從而提高插入性能

注:數據的重排可能會出現頁分裂。比如,將一條記錄插入到一個數據頁中的兩條記錄之間時,此時數據頁已經滿了,根據 B+ 樹的算法,這時候需要申請一個新的數據頁,然后挪動部分數據過去。這個過程稱為頁分裂。

4.6 索引失效情況:

  • 查詢語句中使用LIKE關鍵字:在查詢語句中使用 LIKE 關鍵字進行查詢時,如果匹配字符串的第一個字符為“%”,索引不會被使用。如果“%”不是在第一個位置,索引就會被使用。
  • 查詢語句中使用多列索引:多列索引是在表的多個字段上創建一個索引,只有查詢條件中使用了這些字段中的第一個字段,索引才會被使用。
  • 查詢語句中使用OR關鍵字:查詢語句只有OR關鍵字時,如果OR前后的兩個條件的列都是索引,那么查詢中將使用索引。如果OR前后有一個條件的列不是索引,那么查詢中將不使用索引。

?

4.7 主鍵和唯一索引的區別:

  • 主鍵用于唯一標識表中的一行記錄,唯一索引是索引的一種,用來優化查詢速度;
  • 一張表中只可以有一個主鍵,但是可以有多個唯一索引;
  • 主鍵一定會創建一個唯一索引,但是有唯一索引的列不一定是主鍵;
  • 主鍵中的值不可為null,唯一索引允許出現null值。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/87118.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/87118.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/87118.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[考研408數據結構]王道大題暑假自用復習記錄(每日更新...)

DAY1 2025年6月29日 雨轉晴🌧🌤 第二章 線性表 2.2線性表的順序表示 1、從順序表中刪除具有最小值的元素(假設唯一)并由函數返回被刪元素的值。空出的位置由最后一個元素填補,若順序表為空,則顯示出錯信…

vue2 el-select下拉選擇框 點擊其他位置或者彈窗關閉下拉框/點擊取消時,下拉框變成之前的值

1.elSelect點擊空白處無法收起下拉框(失去焦點并隱藏) // 定義指令 directives: {clickOutside: {bind: function (el, binding, vnode) {el.clickOutsideEvent function (event) { // here I check that click was outside the el and his childrensif…

MYSQL-JAVAweb1

1.登錄 在黑框中輸入 net start mysql // 啟動mysql服務 net stop mysql // 停止mysql服務1.MySQL數據模型 關系型數據庫: 關系型數據庫是建立在關系模型基礎上的數據庫,簡單說,關系型數據庫是由多張能互相連接的 二維表 組成的數據庫 如…

將POD指定具體機器上運行

在Kubernetes中,你可以通過多種方式將Pod調度到指定的節點(機器)上運行。以下是幾種常用的方法及其適用場景: 1. NodeSelector(簡單標簽匹配) 通過標簽選擇器將Pod綁定到具有特定標簽的節點。 步驟 為目…

eNSP實驗一:IPv4編址及IPv4路由基礎

一、實驗目的&#xff1a; 配置各路由器上的物理接口的IP地址并實現互聯互通配置各路由器的 Loopback 的IP地址并實現互聯互通&#xff08;包括備份路由&#xff0c;默認路由&#xff09;圖中三個路由器型號為 AR3620。 二、配置物理接口ip 基礎配置 設備命名<Huawei>…

基于自然語言處理(NLP)的Twitter情感分析系統

本課題致力于構建一個基于自然語言處理&#xff08;NLP&#xff09;與機器學習技術的Twitter情感分析系統&#xff0c;旨在自動識別用戶推文中的主觀情緒傾向&#xff0c;如正面、負面或中性。研究過程中將對海量Twitter文本數據進行預處理&#xff0c;包括去除噪聲、分詞、詞性…

H.264中片數據分割(Slice Data Partitioning)介紹

H.264中**片數據分割&#xff08;Slice Data Partitioning&#xff09;**的解碼機制。讓我為您詳細解析&#xff1a; 1. 片數據&#xff08;Slice Data Partitioning&#xff09;分割的概念 片數據分割是H.264中的一種錯誤恢復機制&#xff0c;通過將片數據分成不同的部分&am…

muduo

好的&#xff0c;我們來深入剖析陳碩老師開發的著名C網絡庫——muduo。它以“簡單、高效、易用”著稱&#xff0c;是學習Linux C高性能網絡編程的絕佳范本。我會盡量詳細、通俗地講解其核心思想、關鍵組件、源碼結構和工作原理。 核心思想&#xff1a;Reactor 模式 (Non-block…

將目錄下所有圖像中非0像素值改為1或者255

圖像二值化處理技術大綱 目標與背景 解釋圖像二值化的意義,分析將非零像素值統一調整為1或255的應用場景(如簡化數據、增強特征、適配模型輸入等)。 核心方法概述 列舉常見圖像格式(如PNG、JPEG)的像素值范圍,說明非零像素的定義(RGB或灰度圖像中的非黑像素)。 方…

Reactor ConnectableFlux支持多訂閱者

在 Reactor 中&#xff0c;ConnectableFlux 是一種用于處理響應式流的機制&#xff0c;它允許你控制何時開始訂閱和數據生成。通常情況下&#xff0c;訂閱者&#xff08;subscriber&#xff09;在訂閱時會立即開始接收數據&#xff0c;但有時你可能希望多個訂閱者“會面”&…

vite + vue 項目下使用 tailwindcss

版本 node: > 18.0.0 vue: 3.5.13 vite: 6.3.1 tailwindcss: 4.1.6 tailwindcss/vite: 4.1.6 tailwindcss ? 細粒度類庫 提供數千個原子級CSS類&#xff08;如 text-center、bg-blue-500、p-4&#xff09;&#x1f9e9; 組合式開發 通過類名組合構建完全自定義的UI&#x…

Hibernate中save與saveOrUpdate的差異解析

在Hibernate中&#xff0c;save()和saveOrUpdate()都是用于持久化對象的方法&#xff0c;但它們的適用場景和行為有顯著差異&#xff1a; 1. save()方法 核心行為&#xff1a; 僅適用于瞬時態&#xff08;Transient&#xff09;對象&#xff08;即新創建、未與Session關聯的對象…

香橙派3B學習筆記14:deb 打包程序_解包前后腳本運行

本文學習如何用deb打包的方式打包自己需要調用系統庫的程序。 然后實現deb解包前后的腳本運行。 目錄 承接上文&#xff1a; 刪除上文遺留的.so文件&#xff1a; 終止ledlight進程&#xff1a; 目標解釋&#xff1a; 創建項目結構&#xff1a; 創建control文件&#xff1a; 創…

nanoGPT復現——prepare拆解(自己構建詞表 VS tiktoken)

在nanoGPT的data文件夾有兩個很相似的文件夾結構&#xff1a;shakespeare和shakespeare-char&#xff0c;這兩種都是對shakespeare數據集的處理&#xff0c;但是shakespeare使用的是tiktoken對文字進行編碼&#xff0c;另一個則是使用自己構建的詞表 一、shakespeare-char&…

macos 安裝 xcode

在 macOS 上安裝 Xcode&#xff08;或者 Xcode Command Line Tools&#xff09;的方法如下&#xff1a; 1. 安裝 Xcode Command Line Tools&#xff08;輕量級&#xff0c;滿足大部分編譯需求&#xff09; 終端命令&#xff1a; xcode-select --install會彈出安裝提示&#x…

大學專業科普 | 云計算、大數據

大數據專業是近年來隨著信息技術發展而興起的熱門學科&#xff0c;專注于從海量、多樣化的數據中提取有價值信息&#xff0c;為各行業提供數據驅動的決策支持。 專業定義 大數據專業旨在培養掌握大數據采集、存儲、管理、分析和應用等核心技術的人才。該專業融合了計算機科學…

本地文件自動提交到倉庫

背景 將本地目錄做一個存儲倉庫&#xff0c;將歸檔的文件放入其中。自動同步到遠程倉庫。 倉庫配置 省略 配置密鑰 用戶可以 git pull \ git push \ git commit 自動 拉取、更新 腳本 文件名&#xff1a;autosave.sh #!/bin/zsh# 設置變量 LOCAL_DIR$1# 進入工作目錄 cd "…

Ubuntu中控制用戶存儲空間配置步驟

目的&#xff0c;限制用戶磁盤空間占用&#xff0c;例如給用戶限制100-150G容量 1.安裝磁盤配額工具 sudo apt-get install -y quota 2.備份并修改/etc/fstab文件&#xff0c;使能支持quota sudo cp /etc/fstab /etc/fstab.bak vim /etc/fstab #寫入如下,usrjquotaaquota.u…

【網絡】Linux 內核優化實戰 - net.ipv4.tcp_rmem 和 net.core.rmem_default 關系

net.ipv4.tcp_rmem 和 net.core.rmem_default 都是 Linux 內核中控制網絡接收緩沖區的參數,但它們的作用范圍、優先級和使用場景存在明顯區別。以下是詳細對比: 核心區別 參數net.ipv4.tcp_rmemnet.core.rmem_default作用協議僅針對 TCP 協議針對 所有網絡協議(TCP、UDP 等…

設計模式精講 Day 14:命令模式(Command Pattern)

【設計模式精講 Day 14】命令模式&#xff08;Command Pattern&#xff09; 文章內容 在“設計模式精講”系列的第14天&#xff0c;我們來學習命令模式&#xff08;Command Pattern&#xff09;。命令模式是一種行為型設計模式&#xff0c;它將請求封裝為對象&#xff0c;從而…