日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql索引的数据结构

發布時間:2024/8/1 数据库 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql索引的数据结构 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為什么使用索引

我們假如不使用索引的話,就像我們左邊的這樣,造成全文索引
加入索引的話,像我們右邊的這樣,那么它的速度就會快上很多。

打個比方,假如我們需要查字典的話,索引就像我們的目錄一樣,沒有索引,我們就只能一頁一頁去找。而加上索引之后,我們可以根據目錄來快速查找我們所需要的東西

對字段Col2添加了索引,就相當于在硬盤上為col 2維護了一個索引的數據結構,即這個二叉搜索樹。二叉搜索樹的每個結點存儲的是(K,V)結構,key是Col 2,value是該key所在行的文件指針(地址)。比如:該二叉搜索樹的根節點就是:(34,0x07)。現在對Ccol2添加了索引,這時再去查找Col 2=89這條記錄的時候會先去查找該二叉搜索樹(二叉樹的遍歷查找)。讀34到內存,89>34;繼續右側數據,讀s9到內存,89 ==- 89;找到數據返回。找到之后就根據當前結點的value快速定位到要查找的記錄對應的地址。我們可以發現,只需要查找兩次就可以定位到記錄的地址,查詢速度就提高了。

這就是我們為什么要建索引,目的就是為了減少磁盤 I/O 的次數,加快查詢速率。

索引及其優缺點

索引概述

MySQL官方對索引的定義為:索引(Index)是幫助MySQL高效獲取數據的數據結構
索引的本質:索引是數據結構。你可以簡單理解為“排好序的快速查找數據結構”,滿足特定查找算法。這些數據結構以某種方式指向數據, 這樣就可以在這些數據結構的基礎上實現 高級查找算法

優點:

(1)類似大學圖書館建書目索引,提高數據檢索的效率,降低 數據庫的IO成本 ,這也是創建索引最主要的原因。
(2)通過創建唯一索引,可以保證數據庫表中每一行 數據的唯一性
(3)在實現數據的參考完整性方面,可以 加速表和表之間的連接。換句話說,對于有依賴關系的子表和父表聯合查詢時,可以提高查詢速度
(4)在使用分組和排序子句進行數據查詢時,可以顯著 減少查詢中分組和排序的時間 ,降低了CPU的消耗

缺點:

(1)創建索引和維護索引要 耗費時間 ,并且隨著數據量的增加,所耗費的時間也會增加。
(2)索引需要占 磁盤空間 ,除了數據表占數據空間之外,每一個索引還要占一定的物理空間, 存儲在磁盤上 ,如果有大量的索引,索引文件就可能比數據文件更快達到最大文件尺寸。
(3)雖然索引大大提高了查詢速度,同時卻會 降低更新表的速度 。當對表中的數據進行增加、刪除和修改的時候,索引也要動態地維護,這樣就降低了數據的維護速度。

提示:
索引可以提高查詢的速度,但是會影響插入記錄的速度。這種情況下,最好的辦法是先刪除表中的索引,然后插入數據,插入完成后再創建索引。

需要注意的是:索引是在具體的存儲引擎中實現的,不同的存儲引擎,索引的數據結構就有可能不一樣。

InnoDB中的索引

這個數據結構,就是我們的B+樹

B+Tree

不論是存放用戶記錄的數據頁,還是存放目錄項記錄的數據頁,我們都把它們存放到B+樹這個數據結構中了,所以我們也稱這些數據頁為節點。從圖中可以看出,我們的實際用戶記錄其實都存放在B+樹的最底層的節點上,這些節點也被稱為葉子節點,其余用來存放目錄項的節點稱為非葉子節點或者內節點,其中B+樹最上邊的那個節點也稱為根節點。

為什么說我們用到的B+樹都不會超過四層呢???

在 MySQL 中我們的 InnoDB 頁的大小默認是 16kb。
假設所有存放用戶記錄的葉子節點代表的數據頁可以存放100條用戶記錄,相當于一條數據占160字節,所有存放目錄項記錄的內節點代表的數據頁可以存放1000條目錄項記錄(因為目錄頁的字段沒有葉子結點的字段多,所以一般可以多存儲一點) ,那么:

  • 如果B+樹只有1層,也就是只有1個用于存放用戶記錄的節點,最多能存放 100 條記錄
  • 如果B+樹有2層,最多能存放 1000×100=10,0000 條記錄。
  • 如果B+樹有3層,最多能存放 1000×1000×100=1,0000,0000 條記錄。
  • 如果B+樹有4層,最多能存放 1000×1000×1000×100=1000,0000,0000 條記錄。相當多的記錄!!!

你的表里能存放一千萬條記錄嗎??所以一般情況下,我們 用到的B+樹都不會超過4層 ,那我們通過主鍵值去查找某條記錄最多只需要做4個頁面內的查找(查找3個目錄項頁和一個用戶記錄頁),又因為在每個頁面內有所謂的 Page Directory(頁目錄),所以在頁面內也可以通過 二分法 實現快速定位記錄。

常見索引概念

索引按照物理實現方式,索引可以分為 2 種:聚簇(聚集)和非聚簇(非聚集)索引。我們也把非聚集索引稱為二級索引或者輔助索引。

聚簇索引

聚簇索引并不是一種單獨的索引類型,而是一種數據存儲方式(所有的用戶記錄都存儲在了葉子節點),也就是所謂的索引即數據,數據即索引

術語"聚簇"表示數據行和相鄰的鍵值聚簇的存儲在一起。

特點:

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

我們把具有這兩種特性的B+樹稱為聚簇索引,所有完整的用戶記錄都存放在這個聚簇索引的葉子節點處。這種聚簇索引并不需要我們在MysQL語句中顯式的使用INDEX語句去創建,InnoDB存儲引擎會自動的為我們創建聚簇索引。

優點:

  • 數據訪問更快 ,因為聚簇索引將索引和數據保存在同一個B+樹中,因此從聚簇索引中獲取數據比非聚簇索引更快
  • 聚簇索引對于主鍵的 排序查找 和 范圍查找 速度非常快
  • 按照聚簇索引排列順序,查詢顯示一定范圍數據的時候,由于數據都是緊密相連,數據庫不用從多個數據塊中提取數據,所以 節省了大量的io操作

缺點:

  • 插入速度嚴重依賴于插入順序 ,按照主鍵的順序插入是最快的方式,否則將會出現頁分裂,嚴重影響性能。因此,對于InnoDB表,我們一般都會定義一個自增的ID列為主鍵
  • 更新主鍵的代價很高 ,因為將會導致被更新的行移動。因此,對于InnoDB表,我們一般定義主鍵為不可更新
  • 二級索引訪問需要兩次索引查找 ,第一次找到主鍵值,第二次根據主鍵值找到行數據

限制:

  • 對于MySQL數據庫目前只有InnoDB數據引擎支持聚簇索引,而MylSAM并不支持聚簇索引。
  • 由于數據物理存儲排序方式只能有一種,所以每個MySQL的表只能有一個聚簇索引。一般情況下就是該表的主鍵。
  • 如果沒有定義主鍵,Innodb會選擇非空的唯一索引代替。如果沒有這樣的索引,Innodb會隱式的定義一個主鍵來作為聚簇索引。
  • 為了充分利用聚簇索引的聚簇的特性,所以innodb表的主鍵列盡量選用有序的順序id,而不建議用無序的id,比如UUID、MD5、HASH、字符串列作為主鍵無法保證數據的順序增長。

二級索引(輔助索引、非聚簇索引)

上邊介紹的聚簇索引只能在搜索條件是主鍵值時才能發揮作用,因為B+樹中的數據都是按照主鍵進行排序的。那如果我們想以別的列作為搜索條件該怎么辦呢?肯定不能是從頭到尾沿著鏈表依次遍歷記錄一遍。

答案:我們可以多建幾棵B+樹,不同的B+樹中的數據采用不同的排序規則。比方說我們用c2列的大小作為數據頁、頁中記錄的排序規則,再建一棵B+樹,效果如下圖所示:

現在我們查找從c2為4(c2為非主鍵)的記錄,先從頂部開始找,最后找到頁34,頁35,如下圖:

現在我們知道了c2==4的三條記錄,但是字段只有c1,c2,我們要查找到它的全部記錄,就需要把c1字段的值拿去聚簇索引里面查找,這個過程就叫做回表

回表

我們根據這個以c2列大小排序的B+樹只能確定我們要查找記錄的主鍵值,所以如果我們想根據c2列的值查找到完整的用戶記錄的話,仍然需要到聚簇索引中再查一遍,這個過程稱為回表。也就是根據c2列的值查詢一條完整的用戶記錄需要使用到2棵B+樹!|

問題:為什么我們還需要一次回表操作呢?直接把完整的用戶記錄放到葉子節點不OK嗎?

如果把完整的用戶記錄放到葉子節點是可以不用回表。但是太占地方了,相當于每建立一棵B+樹都需要把所有的用戶記錄再都拷貝一遍,這就有點太浪費存儲空間了。
我們一個表中只能有一個聚簇索引,而可以有多個二級索引,這樣的話我們有多個二級索引時,還需要多復制幾遍所有的用戶記錄,占得位置大

因為這種按照非主鍵列建立的B+樹需要一次回表操作才可以定位到完整的用戶記錄,所以這種B+樹也被稱為二級索引(英文名secondary index ),或者輔助索引。由于我們使用的是c2列的大小作為B+樹的排序規則,所以我們也稱這個B+樹是為c2列建立的索引。

非聚簇索引的存在不影響數據在聚簇索引中的組織,所以一張表可以有多個非聚簇索引。

小結:聚簇索引與非聚簇索引的原理不同,在使用上也有一些區別:

  • 1.聚簇索引的葉子節點存儲的就是我們的數據記錄,非聚簇索引的葉子節點存儲的是數據位置。非聚簇索引不會影響數據表的物理存儲順序。
  • 2.一個表只能有一個聚簇索引,因為只能有一種排序存儲的方式,但可以有多個非聚簇索引,也就是多個索引目錄提供數據檢索。
  • 3.使用聚簇索引的時候,數據的查詢效率高,但如果對數據進行插入,刪除,更新等操作,效率會比非聚簇索引低(這里暫時不太清楚)。

聯合索引

我們也可以同時以多個列的大小作為排序規則,也就是同時為多個列建立索引,比方說我們想讓B+樹按照 c2和c3列 的大小進行排序,這個包含兩層含義:

  • 先把各個記錄和頁按照c2列進行排序。.
  • 在記錄的c2列相同的情況下,采用c3列進行排序

注意一點,以c2和c3列的大小為排序規則建立的B+樹稱為 聯合索引 ,本質上也是一個二級索引。它的意思與分別為c2和c3列分別建立索引的表述是不同的,不同點如下:

  • 建立 聯合索引 只會建立如上圖一樣的1棵B+樹。
  • 為c2和c3列分別建立索引會分別以c2和c3列的大小為排序規則建立2棵B+樹。

InnoDB的B+樹索引的注意事項

  • 根頁面位置萬年不動
    我們前邊介紹B+樹索引的時候,為了大家理解上的方便,先把存儲用戶記錄的葉子節點都畫出來,然后接著畫存儲目錄項記錄的內節點,實際上B+樹的形成過程是這樣的:
    1.每當為某個表創建一個B+樹索引(聚簇索引不是人為創建的,默認就有)的時候,都會為這個索引創建一個根節點頁面。最開始表中沒有數據的時候,每個B+樹索引對應的根節點中既沒有用戶記錄,也沒有目錄項記錄
    2.隨后向表中插入用戶記錄時,先把用戶記錄存儲到這個根節點中。
    3.當根節點中的可用空間用完時繼續插入記錄,此時會將根節點中的所有記錄復制到一個新分配的頁,比如頁a中,然后對這個新頁進行頁分裂的操作,得到另一個新頁,比如頁b。這時新插入的記錄根據鍵值(也就是聚簇索引中的主鍵值,二級索引中對應的索引列的值)的大小就會被分配到頁a或者b中,而根節點便升級為存儲目錄項記錄的頁。
    這個過程特別注意的是:一個B+樹索引的根節點自誕生之日起,便不會再移動。這樣只要我們對某個表建立一個索引,那么它的根節點的頁號便會被記錄到某個地方,然后凡是InnoDB存儲引擎需要用到這個索引的時候,都會從那個固定的地方取出根節點的頁號,從而來訪問這個索引。
  • 內節點中目錄項記錄的唯一性
    我們知道B+樹索引的內節點中目錄項記錄的內容是索引列+頁號的搭配,但是這個搭配對于二級索引來說有點兒不嚴謹。還拿index_demo表為例,假設這個表中的數據是這樣的:

    如果二級索引中目錄項記錄的內容只是索引列+頁號的搭配的話,那么為c2列建立索引后的B+樹應該長這樣:

    如果我們想新插入一行記錄,其中c1、c2、c3的值分別是:9、1、 ‘c’,那么在修改這個為c2列建立的二級索引對應的B+樹時便碰到了個大問題:由于頁3中存儲的目錄項記錄是由c2列+頁號的值構成的,頁3中的兩條目錄項記錄對應的c2列的值都是1,而我們新插入的這條記錄的c2列的值也是1,那我們這條新插入的記錄到底應該放到頁4中,還是應該放到頁5中啊?答案是:對不起,懵了。
    為了讓新插入記錄能找到自己在那個頁里,我們需要保證在B+樹的同一層內節點的目錄項記錄除頁號這個字段以外是唯一的。所以對于二級索引的內節點的目錄項記錄的內容實際上是由三個部分構成的:
    索引列
    主鍵值
    頁號
    也就是我們把主鍵值也添加到二級索引內節點中的目錄項記錄了,這樣就能保證B+樹每一層節點中各條目錄項記錄除頁號這個字段外是唯一的,所以我們為c2列建立二級索引后的示意圖實際上應該是這樣子的:

    這樣我們再插入記錄(9,1, ‘c’)時,由于頁3中存儲的目錄項記錄是由c2列+主鍵+頁號的值構成的,可以先把新記錄的c2列的值和頁3中各目錄項記錄的c2列的值作比較,如果c2列的值相同的話,可以接著比較主鍵值,因為B+樹同一層中不同目錄項記錄的c2列+主鍵的值肯定是不一樣的,所以最后肯定能定位唯一的一條目錄項記錄,在本例中最后確定新記錄應該被插入到頁5中。(所以我們的二級索引在目錄節點時,也有保存其主鍵值)
  • 一個頁面最少存儲2條記錄
    一個B+樹只需要很少的層級就可以輕松存儲數億條記錄,查詢速度相當不錯!這是因為B+樹本質上就是一個大的多層級目錄,每經過一個目錄時都會過濾掉許多無效的子目錄,直到最后訪問到存儲真實數據的目錄。那如果一個大的目錄中只存放一個子目錄是個啥效果呢?那就是目錄層級非常非常非常多,而且最后的那個存放真實數據的目錄中只能存放一條記錄。費了半天勁只能存放一條真實的用戶記錄?所以InnoDB的一個數據頁至少可以存放兩條記錄。
  • MyLSAM中的索引方案

    我們知道InnoDB中索引即數據,也就是聚簇索引的那棵B+樹的葉子節點中已經把所有完整的用戶記錄都包含了,而MyISAM的索引方案雖然也使用樹形結構,但是卻將索引和數據分開存儲:

    • 將表中的記錄按照記錄的插入順序單獨存儲在一個文件中,稱之為數據文件。這個文件并不劃分為若干個數據頁,有多少記錄就往這個文件中塞多少記錄就成了。由于在插入數據的時候并沒有刻意按照主鍵大小排序,所以我們并不能在這些數據上使用二分法進行查找。
    • 使用”MyISAN存儲引擎的表會把索引信息另外存儲到一個稱為索引文件的另一個文件中。MyISAM會單獨為表的主鍵創建一個索引,只不過在索引的葉子節點中存儲的不是完整的用戶記錄,而是主鍵值+數據記錄地址的組合。

      這里設表一共有三列,假設我們以col1為主鍵,上圖是一個MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主鍵索引和二級索引 (Secondary key)在結構上沒有任何區別,只是主鍵索引要求key是唯一的,而二級索引的key可以重復。如果我們在col2上建立一個二級索引,則此索引的結構如下圖所示:

      同樣也是一棵B+Tree,data域保存數據記錄的地址。因此,MyISAM中索引檢索的算法為:首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然后以data域的值為地址,讀取相應數據記錄。

    MylSAM與InnoDB對比

    MyISAM的索引方式都是“非聚簇”的,與InnoDB包含1個聚簇索引是不同的。小結兩種引擎中索引的區別:

  • 在InnoDB存儲引擎中,我們只需要根據主鍵值對聚簇索引進行一次查找就能找到對應的記錄,而在MyISAM中,卻需要進行一次回表操作,意味著MylSAM中建立的索引相當于全部都是二級索引。
  • InnoDB的數據文件本身就是索引文件,而MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。
  • InnoDB的非聚簇索引data域存儲相應記錄主鍵的值,而MyISAM索引記錄的是地址。換句話說lnnoDB的所有非聚簇索引都引用主鍵作為data域。
  • MyISAM的回表操作是十分快速的,因為是拿著地址偏移量直接到文件中取數據的,反觀InnoDB是通過獲取主鍵之后再去聚簇索引里找記錄,雖然說也不慢,但還是比不上直接用地址去訪問。
  • InnoDB要求表必須有主鍵(_MyISAM可以沒有)。如果沒有顯式指定,則MySQL系統會自動選擇一個可以非空且唯一標識數據記錄的列作為主鍵。如果不存在這種列,則MySQL自動為InnoDB表生成一個隱含字段作為主鍵,這個字段長度為6個字節,類型為長整型。
  • 小結

    了解不同存儲引擎的索引實現方式對于正確使用和優化索引都非常有幫助。比如:

  • 舉例1:知道了InnoDB的索引實現后,就很容易明白為什么不建議使用過長的字段作為主鍵,因為所有二級索引都引用主鍵索引,過長的主鍵索引會令二級索引變得過大。
  • 舉例2:用非單調的字段作為主鍵在innoDB中不是個好主意,因為InnoDB數據文件本身是一棵B+Tree,非單調的主鍵會造成在插入新記錄時,數據文件為了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段作為主鍵則是一個很好的選擇。
  • 索引的代價

    索引是個好東西,可不能亂建,它在空間和時間上都會有消耗:

    • 空間上的代價
      每建立一個索引都要為它建立一棵B+樹,每一棵B+樹的每一個節點都是一個數據頁,一個頁默認會占用16KB的存儲空間,一棵很大的B+樹由許多數據頁組成,那就是很大的一片存儲空間。
    • 時間上的代價
      每次對表中的數據進行增、刪、改操作時,都需要去修改各個B+樹索引。而且我們講過,B+樹每層節點都是按照索引列的值從小到大的順序排序而組成了雙向鏈表。不論是葉子節點中的記錄,還是內節點中的記錄(也就是不論是用戶記錄還是目錄項記錄)都是按照索引列的值從小到大的順序而形成了一個單向鏈表。而增、刪、改操作可能會對節點和記錄的排序造成破壞,所以存儲引擎需要額外的時間進行一些記錄移位,頁面分裂、頁面回收等操作來維護好節點和記錄的排序。如果我們建了許多索引,每個索引對應的B+樹都要進行相關的維護操作,會給性能拖后腿。

    一個表上索引建的越多,就會占用越多的存儲空間,在增刪改記錄的時候性能就越差。為了能建立又好又少的索引,我們得學學這些索引在哪些條件下起作用的。

    MySQL數據結構選擇的合理性

    從MysQL的角度講,不得不考慮一個現實問題就是磁盤l0。如果我們能讓索引的數據結構盡量減少硬盤的l/o操作,所消耗的時間也就越小。可以說,磁盤的 I/0操作次數對索引的使用效率至關重要。

    查找都是索引操作,一般來說索引非常大,尤其是關系型數據庫,當數據量比較大的時候,索引的大小有可能幾個G甚至更多,為了減少索引在內存的占用,數據庫索引是存儲在外部磁盤上的。當我們利用索引查詢的時候,不可能把整個索引全部加載到內存,只能逐一加載,那么MySQL衡量查詢效率的標準就是磁盤I0次數。

    Hash結構

    Hash本身是一個函數,又被稱為散列函數,它可以幫助我們大幅提升檢索數據的效率。

    Hash算法是通過某種確定性的算法(比如MD5、SHA1、SHA2、SHA3)將輸入轉變為輸出。相同的輸入永遠可以得到相同的輸出,假設輸入內容有微小偏差,在輸出中通常會有不同的結果。

    舉例:如果你想要驗證兩個文件是否相同,那么你不需要把兩份文件直接拿來比對,只需要讓對方把 Hash函數計算得到的結果告訴你即可,然后在本地同樣對文件進行Hash 函數的運算,最后通過比較這兩個Hash 函數的結果是否相同,就可以知道這兩個文件是否相同。

    加速查找速度的數據結構,常見的有兩類:

  • 樹,例如平衡二叉搜索樹,查詢/插入/修改/刪除的平均時間復雜度都是o(log2N)
  • 哈希,例如HashMap,查詢/插入/修改/刪除的平均時間復雜度都是o(1)(key,value)

    采用Hash進行檢索效率非常高,基本上一次檢索就可以找到數據,而B樹需要自頂向下依次查找,多次訪問節點才能找到數據,中間需要多次I/o操作,從效率來說Hash 比 B+樹更快
  • 在哈希的方式下,一個元素k處于h(k)中,即利用哈希函數h,根據關鍵字k計算出槽的位置。函數h將關鍵字域映射到哈希表T[o…m-1]的槽位上。

    上圖中哈希函數h有可能將兩個不同的關鍵字映射到相同的位置,這叫做碰撞,在數據庫中一般采用鏈接法來解決。在鏈接法中,將散列到同一槽位的元素放在一個鏈表中,如下圖所示:

    Hash結構效率高,那為什么索引結構要設計成樹型呢?

  • 原因1: Hash索引僅能滿足(=)(<>)和IN查詢。如果進行范圍查詢,哈希型的索引,時間復雜度會退化為o(n);而樹型的“有序”特性,依然能夠保持o(log2N)的高效率。
  • 原因2: Hash索引還有一個缺陷,數據的存儲是沒有順序的,在ORDER BY的情況下,使用Hash索引還需要對數據重新排序。
  • 原因3:對于聯合索引的情況,Hash值是將聯合索引鍵合并后一起來計算的,無法對單獨的一個鍵或者幾個索引鍵進行查詢。
  • 原因4∶對于等值查詢來說,通常Hash索引的效率更高,不過也存在一種情況,就是索引列的重復值如果很多,效率就會降低。這是因為遇到Hash沖突時,需要遍歷桶中的行指針來進行比較,找到查詢的關鍵字,非常耗時。所以,Hash索引通常不會用到重復值多的列上,比如列為性別、年齡的情況等。
  • Hash索引適用存儲引擎如表所示:

    Hash索引的適用性:

  • Hash索引存在著很多限制,相比之下在數據庫中B+樹索引的使用面會更廣,不過也有一些場景采用Hash索引效率更高,比如在鍵值型(Key-Value)數據庫中,Redis 存儲的核心就是 Hash表。
  • MySQL中的Memory存儲引擎支持Hash存儲,如果我們需要用到查詢的臨時表時,就可以選擇Memory存儲引擎,把某個字段設置為Hash 索引,比如字符串類型的字段,進行Hash計算之后長度可以縮短到幾個字節。當字段的重復度低,而且經常需要進行等值查詢的時候,采用Hash索引是個不錯的選擇。
  • 另外,InnoDB本身不支持Hash 索引,但是提供自適應 Hash索引(Adaptive Hash Index)。什么情況下才會使用自適應Hash索引呢?如果某個數據經常被訪問,當滿足一定條件的時候,就會將這個數據頁的地址存放到Hash表中。這樣下次查詢的時候,就可以直接找到這個頁面的所在位置。這樣讓B+樹也具備了Hash 索引的優點。

    采用自適應Hash 索引目的是方便根據SQL的查詢條件加速定位到葉子節點,特別是當B+樹比較深的時候,通過自適應Hash索引可以明顯提高數據的檢索效率。
  • 二叉搜索樹

    如果我們利用二叉樹作為索引結構,那么磁盤的Io次數和索引樹的高度是相關的

    二叉搜索樹的特點

  • 一個節點只能有兩個子節點,也就是一個節點度不能超過2
  • 在子節點<本節點;右子節點>=本節點,比我大的向右,比我小的向左
  • 二叉搜索樹也屬于二分查找樹,極端情況下會退化成了一條鏈表,查找數據的時間復雜度變成了0(n)。

    為了提高查詢效率,就需要減少磁盤I0數。為了減少磁盤Io的次數,就需要盡量降低樹的高度,需要把原來“瘦高”的樹結構變的“矮胖”,樹的每層的分叉越多越好。

    AVL樹

    為了解決上面二叉查找樹退化成鏈表的問題,人們提出了平衡二叉搜索樹(Balanced Binary Tree),又稱為AVL樹,它在二叉搜索樹的基礎上增加了約束,具有以下性質:

    • 它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹。

    數據查詢的時間主要依賴于磁盤I/o的次數,如果我們采用二叉樹的形式,即使通過平衡二叉搜索樹進行了改進,樹的深度也是 o(log2n),當n比較大時,深度也是比較高的,比如下圖的情況:

    每訪問一次節點就需要進行一次磁盤Ⅰ/О 操作,對于上面的樹來說,我們需要進行5次I/O操作。雖然平衡二叉樹的效率高,但是樹的深度也同樣高,這就意味著磁盤Ⅳ/О操作次數多,會影響整體數據查詢的效率。

    針對同樣的數據,如果我們把二叉樹改成M叉樹(M>2)呢?當M=3時,同樣的31個節點可以由下面的三叉樹來進行存儲:

    你能看到此時樹的高度降低了,當數據量N大的時候,以及樹的分叉數M大的時候,M叉樹的高度會遠小于二叉樹的高度(M>2)。所以,我們需要把樹從“瘦高"變“矮胖”。

    B-Tree

    B樹的英文是Balance Tree,也就是多路平衡查找樹。簡寫為B-Tree(注意橫杠表示這兩個單詞連起來的意思,不是減號)。它的高度遠小于平衡二叉樹的高度。

    B樹作為多路平衡查找樹,它的每一個節點最多可以包括M個子節點,M稱為B樹的階。每個磁盤塊中包括了關鍵字和子節點的指針。如果一個磁盤塊中包括了x個關鍵字,那么指針數就是x+1。對于一個100階的B樹來說,如果有3層的話最多可以存儲約100萬的索引數據。對于大量的索引數據來說,采用B樹的結構是非常適合的,因為樹的高度要遠小于二叉樹的高度。

    你能看出來在B樹的搜索過程中,我們比較的次數并不少,但如果把數據讀取出來然后在內存中進行比較,這個時間就是可以忽略不計的。而讀取磁盤塊本身需要進行I/o操作,消耗的時間比在內存中進行比較所需要的時間要多,是數據查找用時的重要因素。B樹相比于平衡二叉樹來說磁盤Ⅰ/0О操作要少,在數據查詢中比平衡二叉樹效率要高。所以只要樹的高度足夠低,IO次數足夠少,就可以提高查詢性能。

    小結:

  • B樹在插入和刪除節點的時候如果導致樹不平衡,就通過自動調整節點的位置來保持樹的自平衡
  • 關鍵字集合分布在整棵樹中,即吁子節點和非葉子節點都存放數據。搜索有可能在非葉子節點結束
  • 其搜索性能等價于在關鍵字全集內做一次二分查找。
  • B+Tree

    B+樹也是一種多路搜索樹,基于B樹做出了改進,主流的DBMS都支持B+樹的索引方式,比如MySQL。相比于B-Tree,B+Tree適合文件索引系統。

    B+樹和B樹的差異在于以下幾點:

  • 有k個孩子的節點就有k個關鍵字。也就是孩子數量=關鍵字數,而B樹中,孩子數量=關鍵字數+1。
  • 非葉子節點的關鍵字也會同時存在在子節點中,并且是在子節點中所有關鍵字的最大(或最小)。
  • 非葉子節點僅用于索引,不保存數據記錄,跟記錄有關的信息都放在葉子節點中。而B樹中,非葉子節點既保存索引,也保存數據記錄。
  • 所有關鍵字都在葉子節點出現,葉子節點構成一個有序鏈表,而且葉子節點本身按照關鍵字的大小從小到大順序鏈接。
  • 看起來B+樹和B樹的查詢過程差不多,但是B+樹和B樹有個根本的差異在于,B+樹的中間節點并不直接存儲數據。這樣的好處都有什么呢?

  • 首先,B+樹查詢效率更穩定。因為B+樹每次只有訪問到葉子節點才能找到對應的數據,而在B樹中,非葉子節點也會存儲數據,這樣就會造成查詢效率不穩定的情況,有時候訪問到了非葉子節點就可以找到關鍵字,而有時需要訪問到葉子節點才能找到關鍵字。
  • 其次,B+樹的查詢效率更高。這是因為通常B+樹比B樹更矮胖(階數更大,深度更低),查詢所需要的磁盤I/o也會更少。同樣的磁盤頁大小,B+樹可以存儲更多的節點關鍵字。
  • 不僅是對單個關鍵字的查詢上,在查詢范圍上,B+樹的效率也比B樹高。這是因為所有關鍵字都出現在B+樹的葉子節點中,葉子節點之間會有指針,數據又是遞增的,這使得我們范圍查找可以通過指針連接查找。而在B樹中則需要通過中序遍歷才能完成查詢范圍的查找,效率要低很多。
  • B樹和B+樹都可以作為索引的數據結構,在 MySQL中采用的是B+樹。
    但B樹和B+樹各有自己的應用場景,不能說B+樹完全比B樹好,反之亦然。

    思考題:為了減少Io,索引樹會一次性加載嗎?

  • 數據庫索引是存儲在磁盤上的,如果數據量很大,必然導致索引的大小也會很大,超過幾個G。
  • 當我們利用索引查詢時候,是不可能將全部幾個c的索引都加載進內存的,我們能做的只能是:逐一加載每一個磁盤頁,因為磁盤頁對應著索引樹的節點。
  • 思考題:B+樹的存儲能力如何?為何說一般查找行記錄,最多只需1~3次磁盤Io

    InnoDB存儲引擎中頁的大小為16KB,一般表的主鍵類型為INT(占用4個字節)或BIGINT(占用8個字節),指針類型也一般為4或8個字節,也就是說一個頁(B+Tree中的一個節點)中大概存儲16KB/(8B+8B)=1K個鍵值(因為是估值,為方便計算,這里的K取值為103。也就是說一個深度為3的B+Tree索引可以維護103 * 103 * 103= 10億條記錄。(這里假定一個數據頁也存儲103條行記錄數據了)
    實際情況中每個節點可能不能填充滿,因此在數據庫中,B+Tree 的高度一般都在2-4層。MySQL的 InnoDB存儲引擎在設計時是將根節點常駐內存的,也就是說查找某一鍵值的行記錄時最多只需要1~3次磁盤1/o操作。

    思考題:為什么說B+樹比B-樹更適合實際應用中操作系統的文件索引和數據庫索引?

  • B+樹的磁盤讀寫代價更低
    B+樹的內部結點并沒有指向關鍵字具體信息的指針。因此其內部結點相對B樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那么盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的需要查找的關鍵字也就越多。相對來說Io讀寫次數也就降低了。
  • B+樹的查詢效率更加穩定
    由于非終結點并不是最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查找必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當
  • 思考題:Hash索引與B+樹索引的區別??

    我們之前講到過B+樹索引的結構,Hash索引結構和B+樹的不同,因此在索引使用上也會有差別。

  • Hash索引不能進行范圍查詢,而B+樹可以。這是因為Hash索引指向的數據是無序的,而B+樹的葉子節點是個有序的鏈表.
  • Hash索引不支持聯合索引的最左側原則(即聯合索引的部分索引無法使用),而B+樹可以。對于聯合索引來說,Hash 索引在計算Hash值的時候是將索引鍵合并后再一起計算Hash值,所以不會針對每個索引單獨計算Hash值。因此如果用到聯合索引的一個或者幾個索引時,聯合索引無法被利用。
  • Hash索引不支持ORDER BY排序,因為Hash索引指向的數據是無序的,因此無法起到排序優化的作用,而B+樹索引數據是有序的,可以起到對該字段ORDER BY排序優化的作用。同理,我們也無法用Hash索引進行模糊查詢,而B+樹使用LIKE進行模糊查詢的時候,LIKE后面模糊查詢(比如%結尾)的話就可以起到優化作用。
  • InnoDB不支持Hash索引
  • 思考題:Hash索引與B+樹索引是在建索引的時候手動指定的嗎?


    你能看到,針對InnoDB和MyISAM存儲引擎,都會默認采用B+樹索引,無法使用Hash索引。InnoDB提供的自適應Hash是不需要手動指定的。如果是Memory/Heap和NDB存儲引擎,是可以進行選擇Hast索引的

    小結

    • 使用索引可以幫助我們從海量的數據中快速定位想要查找的數據,不過索引也存在一些不足,比如占用存儲空間、降低數據庫寫操作的性能等,如果有多個索引還會增加索引選擇的時間。當我們使用索引時,需要平衡索引的利(提升查詢效率)和弊(維護索引所的代價)。
    • 在實際工作中,我們還需要基于需求和數據本身的分布情況來確定是否使用索引,盡管索引不是萬能的,`但數據量大的時候不使用索引是不可想象的,畢竟索引的本質,是幫助我們提升數據檢索的效率。

    總結

    以上是生活随笔為你收集整理的mysql索引的数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。