在上一節,咱們聊到數據庫爲了讓咱們的查詢加速,經過索引方式對數據進行冗餘並排序,這樣咱們在使用時就能夠在排好序的數據裏進行快速的二分查找,使得查詢效率指數提高。可是我在結尾一樣提到一個問題,就是內存大小通常是頗有限的,不可能把一個表全部的數據都加載到內存中,那麼咱們該如何解決這個問題呢?在解決這個問題以前,須要先簡單瞭解一下硬盤知識html
因爲機械硬盤的高耐久,低成本,如今仍然是數據存儲的主流,因此這裏着重討論機械硬盤,下面是一個機械硬盤結構圖linux
機械硬盤的數據都存放在盤片中,當咱們從硬盤讀取數據時,咱們須要提供一個地址,而後硬盤經過先後移動磁頭尋址,最後把地址對應數據返回。sql
這裏有兩個過程很重要,一個是尋址,一個是讀取數據。以目前機械硬盤的速度,若是咱們要從機械硬盤讀取一條1KB的數據大概只須要0.01ms(100MB/s),而尋址卻平均在10ms左右。一般咱們把讀取一段連續的數據,不須要屢次尋址的操做叫作順序讀,而讀取不連續的數據須要屢次尋址的操做叫作隨機讀,用來區分它們之間的性能差距。數據庫
爲了充分利用機械硬盤的性能,一般把相關數據連續保存,這樣就能夠一次加載更多的數據,減小磁頭的的移動次數。操做系統有不少對此的優化,例如Linux ext3文件系統默認塊大小就是4kb。還有linux預加載能力,即當你頻繁訪問一塊數據時,系統會幫你把相鄰的數據也加載進來。性能
瞭解完機械硬盤的基本知識,如今回到MySQL,MySQL InnoDB引擎也會把數據進行分塊存儲,默認是16KB。因此咱們上一節中的索引結構圖在硬盤中的存儲就是每16KB爲一個塊,當一個塊快存放快滿的時候開闢一個新的塊來存放。學習
以books表爲例優化
create table books( id int not null primary key auto_increment, name varchar(255) not null, author varchar(255) not null, created_at datetime not null default current_timestamp, updated_at datetime not null default current_timestamp on update current_timestamp, index idx_books_name(name) )engine=InnoDB;
該表name字段的索引idx_books_name在硬盤中的存放就以下圖spa
當塊愈來愈多的時候,咱們可能沒法一次把全部的塊都加載到內存,此時就要對每一個塊再進行索引,以下圖:操作系統
每一個塊的上一級都存放着一條指向該塊首記錄的記錄。這樣只須要加載頂部的第一塊,而後經過區間判斷就能夠找到下一塊的地址。3d
例如咱們查詢一條name=name n+1的記錄,過程以下:
1. 先從左邊頂部塊a開始查找,發現"name n+1"在"name 1"到"name m"記錄之間
2. 加載"name 1"對應的下一級塊b
3. 發現"name n+1"在塊b第二條記錄到第三條記錄之間,因此須要加載第二條記錄對應的下一級塊d
4. 加載塊d
5. 在塊d中找到"name n+1"的那條記錄。
若是把上圖旋轉一個,能夠發現,整個圖就是一個樹,這其實就是B+樹。B+樹經過對數據塊進行索引,使得當數據量很大,沒法一次所有加載到內存時,能夠先加載一個表的頂部數據塊,而後根據數據所在區間再加載下一級的數據塊。這樣既保證了咱們的快速搜索,又減小了內存使用。
瞭解了B+樹,如今就能夠很容易區分MySQL的聚簇索引和二級索引。
聚簇索引就是用主鍵生成B+樹,在葉子節點存放這條記錄的完整信息
二級索引就是用索引行生成B+樹,在葉子節點只存放索引行和該行對應的主鍵信息
下面是聚簇索引和二級索引的區分圖
瞭解上面的知識,對於一個查詢,咱們就能夠大概想象出他的執行步驟
select * from books where name = "name400";
例如上面sql的執行步驟以下:
1. 在二級索引idx_books_name索引中查找name="name400"的字段所對應的主鍵id
2. 經過主鍵id在聚簇索引找到此id所對應的記錄
3. 返回記錄中的全部字段
當咱們select的字段在二級索引上不存在時,都須要使用聚簇索引回表查詢剩餘字段。因此聚簇索引,也就是咱們所說的id列,佔用空間越小越好, 這樣就能夠在一個節點中存放更多的id值,減小樹的層級,加速查詢效率。通常推薦主鍵使用int或者bigint而不是字符串。同時最好保證插入的id值爲遞增的,這樣就不會形成在一個已經滿的節點中插入一條記錄形成頁分裂,下降查詢效率。
這節咱們先了解了硬盤的基礎知識,知道了機械硬盤的順序讀與隨機讀的巨大性能差距,以及操做系統爲了優化磁盤性能而把數據進行按塊存儲。而後又學習了MySQL經過使用B+樹,把存放索引的多個數據塊進行索引,解決了咱們上一節使用二分搜索須要先把全部數據都加載到內存的問題。最後,咱們瞭解了聚簇索引和二級索引的區別,以及其中的使用建議。
下一節,咱們會聊一聊如何建立一個好的索引,判斷一個索引的好壞標準有哪些。