接着之前總結的:索引概述入門,今天全面講述一下索引相關知識點
1.數據庫索引是什麼?有什麼優缺點?
索引是一種能提高數據庫查詢效率的有序的數據結構。它可以比作一本字典的目錄,可以幫你快速找到對應的記錄。索引一般存儲在磁盤的文件中,它是佔用物理空間的。其優缺點如下:
優點:
- 加快數據查詢速度,這也是解決慢SQL,創建索引的主要原因。你可以想象查找一個字沒有目錄,一頁一頁去查找和根據目錄快速定位到該字在哪一頁兩者的查詢效率對比。
- 創建唯一索引保證數據的唯一性,從而達到業務邏輯層的冪等性。
缺點:
- 創建索引和維護索引要耗費時間,對錶中的數據進行增、刪、改時都需要動態維護索引。比如說你在字典最後一頁新增了一個漢字,你還需要同時維護目錄。
- 索引需要佔物理空間,除了數據表佔用數據空間之外,每一個索引還要佔用一定的物理空間,所以不能亂建過多索引,會導致數據庫空間佔用過大
2.什麼是聚簇索引與非聚簇索引?區別是什麼?
聚簇索引並不是一種單獨的索引類型,而是一種數據存儲方式。它表示索引結構和數據一起存放的索引。非聚簇索引是索引結構和數據分開存放的索引。
在MySQL
的InnoDB
存儲引擎中, 聚簇索引與非聚簇索引最大的區別,在於葉節點是否存放一整行記錄。聚簇索引葉子節點存儲了一整行記錄,而非聚簇索引葉子節點存儲的是主鍵信息,因此,一般非聚簇索引還需要回表查詢,覆蓋索引不需要。覆蓋索引是select
的數據列只用從索引中就能夠取得,不必回表,換句話說,查詢列要被所建的索引覆蓋
- 一個表中只能擁有一個聚簇索引(因爲一般聚簇索引就是主鍵索引),而非聚簇索引一個表則可以存在多個。
- 一般來說,相對於非聚簇索引,聚簇索引查詢效率更高,因爲不用回表。
而在MyISM
存儲引擎中,它的索引都是非聚簇索引,因爲數據和索引是分開的,葉子節點data存放是數據記錄的指針地址
3.InnoDB索引與MyISAM索引實現的區別是什麼?
MyISAM的索引方式都是非聚簇的,與InnoDB包含1個聚簇索引是不同的。
-
在InnoDB存儲引擎中,我們只需要根據主鍵值對聚簇索引進行一次查找就能找到對應的記錄,而在MyISAM中卻需要進行一次回表操作,意味着MyISAM中建立的索引相當於全部都是二級索引 。
-
InnoDB的數據文件本身就是索引文件,而MyISAM索引文件和數據文件是分離的 ,索引文件僅保存數據記錄的地址。
- MyISAM的表在磁盤上存儲在以下文件中:
*.sdi(描述表結構)
、*.MYD(數據)
,*.MYI(索引)
- InnoDB的表在磁盤上存儲在以下文件中:
.ibd(表結構、索引和數據都存在一起)
-
InnoDB的非聚簇索引data域存儲相應記錄主鍵的值 ,而MyISAM索引記錄的是地址 。換句話說,InnoDB的所有非聚簇索引都引用主鍵作爲data域。
-
MyISAM的回表操作是十分快速的,因爲是拿着地址偏移量直接到文件中取數據的,反觀InnoDB是通過獲取主鍵之後再去聚簇索引裏找記錄,相對來說比不MyISAM直接使用地址訪問快。
-
InnoDB要求表必須有主鍵 ( MyISAM可以沒有 )。如果沒有顯式指定,則MySQL系統會自動選擇一個可以非空且唯一標識數據記錄的列作爲主鍵。如果不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段作爲主鍵,這個字段長度爲6個字節,類型爲長整型。
4.B+樹索引實現原理
MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作爲索引結構
假設有一個表index_demo,表中有2個INT類型的列,1個CHAR(1)類型的列,c1列爲主鍵:
CREATE TABLE index_demo(c1 INT,c2 INT,c3 CHAR(1),PRIMARY KEY(c1)) ;
index_demo表的簡化的行格式示意圖如下:
我們只在示意圖裏展示記錄的這幾個部分:
record_type:
表示記錄的類型, 0是普通記錄、 2是最小記錄、 3 是最大記錄、1是B+樹非葉子節點記錄。next_record:
表示下一條記錄的相對位置,我們用箭頭來表明下一條記錄。各個列的值:
這裏只記錄在 index_demo 表中的三個列,分別是 c1 、 c2 和 c3 。其他信息:
除了上述3種信息以外的所有信息,包括其他隱藏列的值以及記錄的額外信息。
將其他信息
項暫時去掉並把它豎起來的效果就是這樣:
把一些記錄放到頁裏的示意圖就是(這裏一頁就是一個磁盤塊,代表一次IO):
MySQL InnoDB的默認的頁大小是16KB
,因此數據存儲在磁盤中,可能會佔用多個數據頁。如果各個頁中的記錄沒有規律,我們就不得不依次遍歷所有的數據頁。如果我們想快速的定位到需要查找的記錄在哪些數據頁中
,我們可以這樣做 :
- 下一個數據頁中用戶記錄的主鍵值必須大於上一個頁中用戶記錄的主鍵值
- 給所有的頁建立目錄項
以頁28
爲例,它對應目錄項2
,這個目錄項中包含着該頁的頁號28
以及該頁中用戶記錄的最小主鍵值 5
。我們只需要把幾個目錄項在物理存儲器上連續存儲(比如:數組),就可以實現根據主鍵值快速查找某條記錄的功能了。比如:查找主鍵值爲 20 的記錄,具體查找過程分兩步:
- 先從目錄項中根據二分法快速確定出
主鍵值爲20的記錄在目錄項3中
(因爲 12 ≤ 20 < 209 ),對應頁9
。 - 再到頁9中根據二分法快速定位到主鍵值爲 20 的用戶記錄。
至此,針對數據頁做的簡易目錄就搞定了。這個目錄有一個別名,稱爲索引
。
我們新分配一個編號爲30的頁來專門存儲目錄項記錄
,頁10、28、9、20專門存儲用戶記錄
:
目錄項記錄和普通的用戶記錄的不同點:
- 目錄項記錄 的 record_type 值是1,而 普通用戶記錄 的 record_type 值是0。
- 目錄項記錄只有主鍵值和頁的編號兩個列,而普通的用戶記錄的列是用戶自己定義的,包含很多列,另外還有InnoDB自己添加的隱藏列。
現在查找主鍵值爲 20 的記錄,具體查找過程分兩步:
- 先到頁30中通過二分法快速定位到對應目錄項,因爲 12 ≤ 20 < 209 ,就是頁9。
- 再到頁9中根據二分法快速定位到主鍵值爲 20 的用戶記錄。
更復雜的情況如下:
我們生成了一個存儲更高級目錄項的頁33 ,這個頁中的兩條記錄分別代表頁30和頁32,如果用戶記錄的主鍵值在 [1, 320)
之間,則到頁30中查找更詳細的目錄項記錄,如果主鍵值 不小於320 的話,就到頁32中查找更詳細的目錄項記錄。**這個數據結構,它的名稱是 B+樹 **
5.聚簇索引與非聚簇索引b+樹實現有什麼區別?
5.1聚簇索引
特點:
-
索引和數據保存在同一個B+樹中
-
頁內的記錄
是按照主鍵
的大小順序排成一個單向鏈表
。 -
頁和頁之間
也是根據頁中記錄的主鍵
的大小順序排成一個雙向鏈表
。 -
非葉子節點存儲的是記錄的
主鍵+頁號
。 -
葉子節點存儲的是
完整的用戶記錄
。
優點:
- 數據訪問更快 ,因爲
索引和數據保存在同一個B+樹中
,因此從聚簇索引中獲取數據比非聚簇索引更快。 - 聚簇索引對於主鍵的
排序查找
和範圍查找
速度非常快。 - 按照聚簇索引排列順序,查詢顯示一定範圍數據的時候,由於
數據都是緊密相連
,數據庫可以從更少的數據塊中提取數據,節省了大量的IO操作
。
缺點:
- 插入速度嚴重依賴於插入順序 ,按照主鍵的順序插入是最快的方式,否則將會出現頁分裂,嚴重影響性能。因此,對於InnoDB表,我們一般都會定義一個
自增的ID列爲主鍵
。 - 更新主鍵的代價很高 ,因爲將會導致被更新的行移動。因此,對於InnoDB表,我們一般定義
主鍵爲不可更新
。
限制:
- 只有InnoDB引擎支持聚簇索引,
MyISAM不支持聚簇索引
。 - 由於數據的物理存儲排序方式只能有一種,所以
每個MySQL的表只能有一個聚簇索引
。 - 如果沒有爲表定義主鍵,InnoDB會選擇
非空的唯一索引列代替
。如果沒有這樣的列,InnoDB會隱式的定義一個主鍵
作爲聚簇索引。 - 爲了充分利用聚簇索引的聚簇特性,InnoDB中表的
主鍵應選擇有序的id
,不建議使用無序的id,比如UUID、MD5、HASH、字符串作爲主鍵,無法保證數據的順序增長。
5.2 非聚簇索引(二級索引、輔助索引)
聚簇索引
,只能在搜索條件是主鍵值
時才發揮作用,因爲B+樹中的數據都是按照主鍵進行排序的,如果我們想以別的列作爲搜索條件,那麼需要創建非聚簇索引
。
例如,以c2列作爲搜索條件
,那麼需要使用c2列創建一棵B+樹
,如下所示:
這個B+樹與聚簇索引有幾處不同:
-
頁內的記錄
是按照從c2列
的大小順序排成一個單向鏈表
。 -
頁和頁之間
也是根據頁中記錄的c2列
的大小順序排成一個雙向鏈表
。 -
非葉子節點存儲的是記錄的
c2列+頁號
。 -
葉子節點存儲的並不是完整的用戶記錄,而只是
c2列+主鍵
這兩個列的值。
6.一個b+樹中大概能存放多少條索引記錄?
MySQL中一個頁存放的記錄數量是非常大的(默認16KB),假設指針與鍵值忽略不計(或看做10個字節),數據佔 1 kb 的空間:
- 如果B+樹只有1層,也就是隻有1個用於存放用戶記錄的節點,最多能存放 16 條記錄。
- 如果B+樹有2層,最多能存放
1600×16=25600
條記錄。 - 如果B+樹有3層,最多能存放
1600×1600×16=40960000
條記錄。 - 如果存儲千萬級別的數據,只需要三層就夠了
B+樹的非葉子節點不存儲用戶記錄,只存儲目錄記錄,相對B樹每個節點可以存儲更多的記錄,樹的高度會更矮胖,IO次數也會更少。
7.什麼是自適應哈希索引和索引下推
自適應哈希索引
自適應哈希索引是Innodb引擎的一個特殊功能,當MySQL注意到某些索引值被使用的非常頻繁時,會在內存中基於B-Tree所有之上再創建一個哈希索引,這就讓B-Tree索引也具有哈希索引的一些優點,比如快速哈希查找。這是一個完全自動的內部行爲,用戶無法控制或配置。
索引下推
select * from user where name like '小%' and age=18;
其中,name
和age
爲聯合索引(idx_name_age
)
如果是MySQL5.6之前,在idx_name_age
索引樹,找出滿足條件名字第一個字是“小”
的人,然後依次拿着主鍵id回表找出數據記錄,再去對比年齡是否滿足條件,這時候你會age不也在索引中嗎,爲啥不先判斷age是否滿足,再回表查詢對應的數據記錄呢?這樣可以減少回表次數提升性能速度。所以MySQL 5.6
就引入了索引下推優化,可以在索引遍歷過程中,對索引中包含的字段先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數。
8.如何知道語句是否走索引查詢?
explain
查看SQL的執行計劃,這樣就知道是否命中索引了。
當explain
與SQL
一起使用時,MySQL將顯示來自優化器的有關語句執行計劃的信息。
一般來說,我們需要重點關注type、rows、filtered、extra、key
。
8.1 type
type表示連接類型,查看索引執行情況的一個重要指標。以下性能從好到壞依次:system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
- system:這種類型要求數據庫表中只有一條數據,是
const
類型的一個特例,一般情況下是不會出現的。 - const:通過一次索引就能找到數據,一般用於主鍵或唯一索引作爲條件,這類掃描效率極高,,速度非常快。
- eq_ref:常用於主鍵或唯一索引掃描,一般指使用主鍵的關聯查詢
- ref : 常用於非主鍵和唯一索引掃描。
- ref_or_null:這種連接類型類似於
ref
,區別在於MySQL
會額外搜索包含NULL
值的行 - index_merge:使用了索引合併優化方法,查詢使用了兩個以上的索引。
- unique_subquery:類似於
eq_ref
,條件用了in
子查詢 - index_subquery:區別於
unique_subquery
,用於非唯一索引,可以返回重複值。 - range:常用於範圍查詢,比如:between ... and 或 In 等操作
- index:全索引掃描
- ALL:全表掃描
8.2 rows
該列表示MySQL估算要找到我們所需的記錄,需要讀取的行數。對於InnoDB表,此數字是估計值,並非一定是個準確值。
8.3 filtered
該列是一個百分比的值,表裏符合條件的記錄數的百分比。簡單點說,這個字段表示存儲引擎返回的數據在經過過濾後,剩下滿足條件的記錄數量的比例。
8.4 extra
該字段包含有關MySQL如何解析查詢的其他信息,它一般會出現這幾個值:
- Using filesort:表示按文件排序,一般是在指定的排序和索引排序不一致的情況纔會出現。一般見於order by語句
- Using index :表示是否用了覆蓋索引。
- Using temporary: 表示是否使用了臨時表,性能特別差,需要重點優化。一般多見於group by語句,或者union語句。
- Using where : 表示使用了where條件過濾.
- Using index condition:MySQL5.6之後新增的索引下推。在存儲引擎層進行數據過濾,而不是在服務層過濾,利用索引現有的數據減少回表的數據。
8.5 key
該列表示實際用到的索引。一般配合possible_keys
列一起看。
轉自
MySQL高階知識點(三):喫透索引
https://mp.weixin.qq.com/s?__biz=Mzg5MDY1NzI0MQ==&mid=2247485707&idx=1&sn=fa8abc12e7705ec950dc4d34e51442db&chksm=cfd809bdf8af80ab5afc84361257bfd5bd892bdc4838b63d1120bc4b2436d6d04704596126cd&scene=178&cur_album_id=2577655039234932737#rd