[java八股文][MySQL面試篇]索引

索引是什么?有什么好處?

索引類似于書籍的目錄,可以減少掃描的數據量,提高查詢效率。

  • 如果查詢的時候,沒有用到索引就會全表掃描,這時候查詢的時間復雜度是On
  • 如果用到了索引,那么查詢的時候,可以基于二分查找算法,通過索引快速定位到目標數據, mysql 索引的數據結構一般是 b+樹,其搜索復雜度為O(logdN),其中 d 表示節點允許的最大子節點個數為 d 個。

#講講索引的分類是什么?

MySQL可以按照四個角度來分類索引。

  • 按「數據結構」分類:B+tree索引、Hash索引、Full-text索引
  • 按「物理存儲」分類:聚簇索引(主鍵索引)、二級索引(輔助索引)
  • 按「字段特性」分類:主鍵索引、唯一索引、普通索引、前綴索引
  • 按「字段個數」分類:單列索引、聯合索引

接下來,按照這些角度來說說各類索引的特點。

按數據結構分類

從數據結構的角度來看,MySQL 常見索引有 B+Tree 索引、HASH 索引、Full-Text 索引。

每一種存儲引擎支持的索引類型不一定相同,我在表中總結了 MySQL 常見的存儲引擎 InnoDB、MyISAM 和 Memory 分別支持的索引類型。

img

InnoDB 是在 MySQL 5.5 之后成為默認的 MySQL 存儲引擎,B+Tree 索引類型也是 MySQL 存儲引擎采用最多的索引類型。

在創建表時,InnoDB 存儲引擎會根據不同的場景選擇不同的列作為索引:

  • 如果有主鍵,默認會使用主鍵作為聚簇索引的索引鍵(key);
  • 如果沒有主鍵,就選擇第一個不包含 NULL 值的唯一列作為聚簇索引的索引鍵(key);
  • 在上面兩個都沒有的情況下,InnoDB 將自動生成一個隱式自增 id 列作為聚簇索引的索引鍵(key);

其它索引都屬于輔助索引(Secondary Index),也被稱為二級索引或非聚簇索引。創建的主鍵索引和二級索引默認使用的是 B+Tree 索引

按物理存儲分類

從物理存儲的角度來看,索引分為聚簇索引(主鍵索引)、二級索引(輔助索引)。

這兩個區別在前面也提到了:

  • 主鍵索引的 B+Tree 的葉子節點存放的是實際數據,所有完整的用戶記錄都存放在主鍵索引的 B+Tree 的葉子節點里;
  • 二級索引的 B+Tree 的葉子節點存放的是主鍵值,而不是實際數據。

所以,在查詢時使用了二級索引,如果查詢的數據能在二級索引里查詢的到,那么就不需要回表,這個過程就是覆蓋索引。如果查詢的數據不在二級索引里,就會先檢索二級索引,找到對應的葉子節點,獲取到主鍵值后,然后再檢索主鍵索引,就能查詢到數據了,這個過程就是回表。

按字段特性分類

從字段特性的角度來看,索引分為主鍵索引、唯一索引、普通索引、前綴索引。

  • 主鍵索引

主鍵索引就是建立在主鍵字段上的索引,通常在創建表的時候一起創建,一張表最多只有一個主鍵索引,索引列的值不允許有空值。

在創建表時,創建主鍵索引的方式如下:

CREATE TABLE table_name  (....PRIMARY KEY (index_column_1) USING BTREE
);
  • 唯一索引

唯一索引建立在 UNIQUE 字段上的索引,一張表可以有多個唯一索引,索引列的值必須唯一,但是允許有空值。

在創建表時,創建唯一索引的方式如下:

CREATE TABLE table_name  (....UNIQUE KEY(index_column_1,index_column_2,...) 
);

建表后,如果要創建唯一索引,可以使用這面這條命令:

CREATE UNIQUE INDEX index_name
ON table_name(index_column_1,index_column_2,...);
  • 普通索引

普通索引就是建立在普通字段上的索引,既不要求字段為主鍵,也不要求字段為 UNIQUE。

在創建表時,創建普通索引的方式如下:

CREATE TABLE table_name  (....INDEX(index_column_1,index_column_2,...) 
);

建表后,如果要創建普通索引,可以使用這面這條命令:

CREATE INDEX index_name
ON table_name(index_column_1,index_column_2,...);
  • 前綴索引

前綴索引是指對字符類型字段的前幾個字符建立的索引,而不是在整個字段上建立的索引,前綴索引可以建立在字段類型為 char、 varchar、binary、varbinary 的列上。

使用前綴索引的目的是為了減少索引占用的存儲空間,提升查詢效率。

在創建表時,創建前綴索引的方式如下:

CREATE TABLE table_name(column_list,INDEX(column_name(length))
);

建表后,如果要創建前綴索引,可以使用這面這條命令:

CREATE INDEX index_name
ON table_name(column_name(length));

按字段個數分類

從字段個數的角度來看,索引分為單列索引、聯合索引(復合索引)。

  • 建立在單列上的索引稱為單列索引,比如主鍵索引;
  • 建立在多列上的索引稱為聯合索引;

通過將多個字段組合成一個索引,該索引就被稱為聯合索引。

比如,將商品表中的 product_no 和 name 字段組合成聯合索引(product_no, name),創建聯合索引的方式如下:

CREATE INDEX index_product_no_name ON product(product_no, name);

聯合索引(product_no, name) 的 B+Tree 示意圖如下。

可以看到,聯合索引的非葉子節點用兩個字段的值作為 B+Tree 的 key 值。當在聯合索引查詢數據時,先按 product_no 字段比較,在 product_no 相同的情況下再按 name 字段比較。

也就是說,聯合索引查詢的 B+Tree 是先按 product_no 進行排序,然后再 product_no 相同的情況再按 name 字段排序。

因此,使用聯合索引時,存在最左匹配原則,也就是按照最左優先的方式進行索引的匹配。在使用聯合索引進行查詢的時候,如果不遵循「最左匹配原則」,聯合索引會失效,這樣就無法利用到索引快速查詢的特性了。

比如,如果創建了一個 (a, b, c) 聯合索引,如果查詢條件是以下這幾種,就可以匹配上聯合索引:

  • where a=1;
  • where a=1 and b=2 and c=3;
  • where a=1 and b=2;

需要注意的是,因為有查詢優化器,所以 a 字段在 where 子句的順序并不重要。

但是,如果查詢條件是以下這幾種,因為不符合最左匹配原則,所以就無法匹配上聯合索引,聯合索引就會失效:

  • where b=2;
  • where c=3;
  • where b=2 and c=3;

上面這些查詢條件之所以會失效,是因為(a, b, c) 聯合索引,是先按 a 排序,在 a 相同的情況再按 b 排序,在 b 相同的情況再按 c 排序。所以,b 和 c 是全局無序,局部相對有序的,這樣在沒有遵循最左匹配原則的情況下,是無法利用到索引的。

聯合索引有一些特殊情況,并不是查詢過程使用了聯合索引查詢,就代表聯合索引中的所有字段都用到了聯合索引進行索引查詢,也就是可能存在部分字段用到聯合索引的 B+Tree,部分字段沒有用到聯合索引的 B+Tree 的情況。

這種特殊情況就發生在范圍查詢。聯合索引的最左匹配原則會一直向右匹配直到遇到「范圍查詢」就會停止匹配。也就是范圍查詢的字段可以用到聯合索引,但是在范圍查詢字段的后面的字段無法用到聯合索引

#MySQL聚簇索引和非聚簇索引的區別是什么?

img

  • 數據存儲:在聚簇索引中,數據行按照索引鍵值的順序存儲,也就是說,索引的葉子節點包含了實際的數據行。這意味著索引結構本身就是數據的物理存儲結構。非聚簇索引的葉子節點不包含完整的數據行,而是包含指向數據行的指針或主鍵值。數據行本身存儲在聚簇索引中。
  • 索引與數據關系:由于數據與索引緊密相連,當通過聚簇索引查找數據時,可以直接從索引中獲得數據行,而不需要額外的步驟去查找數據所在的位置。當通過非聚簇索引查找數據時,首先在非聚簇索引中找到對應的主鍵值,然后通過這個主鍵值回溯到聚簇索引中查找實際的數據行,這個過程稱為“回表”。
  • 唯一性:聚簇索引通常是基于主鍵構建的,因此每個表只能有一個聚簇索引,因為數據只能有一種物理排序方式。一個表可以有多個非聚簇索引,因為它們不直接影響數據的物理存儲位置。
  • 效率:對于范圍查詢和排序查詢,聚簇索引通常更有效率,因為它避免了額外的尋址開銷。非聚簇索引在使用覆蓋索引進行查詢時效率更高,因為它不需要讀取完整的數據行。但是需要進行回表的操作,使用非聚簇索引效率比較低,因為需要進行額外的回表操作。

#如果聚簇索引的數據更新,它的存儲要不要變化?

  • 如果更新的數據是非索引數據,也就是普通的用戶記錄,那么存儲結構是不會發生變化
  • 如果更新的數據是索引數據,那么存儲結構是有變化的,因為要維護 b+樹的有序性

#MySQL主鍵是聚簇索引嗎?

在MySQL的InnoDB存儲引擎中,主鍵確實是以聚簇索引的形式存儲的。

InnoDB將數據存儲在B+樹的結構中,其中主鍵索引的B+樹就是所謂的聚簇索引。這意味著表中的數據行在物理上是按照主鍵的順序排列的,聚簇索引的葉節點包含了實際的數據行。

img

InnoDB 在創建聚簇索引時,會根據不同的場景選擇不同的列作為索引:

  • 如果有主鍵,默認會使用主鍵作為聚簇索引的索引鍵;
  • 如果沒有主鍵,就選擇第一個不包含 NULL 值的唯一列作為聚簇索引的索引鍵;
  • 在上面兩個都沒有的情況下,InnoDB 將自動生成一個隱式自增 id 列作為聚簇索引的索引鍵;

一張表只能有一個聚簇索引,那為了實現非主鍵字段的快速搜索,就引出了二級索引(非聚簇索引/輔助索引),它也是利用了 B+ 樹的數據結構,但是二級索引的葉子節點存放的是主鍵值,不是實際數據。

#什么字段適合當做主鍵?

  • 字段具有唯一性,且不能為空的特性
  • 字段最好的是有遞增的趨勢的,如果字段的值是隨機無序的,可能會引發頁分裂的問題,造型性能影響。
  • 不建議用業務數據作為主鍵,比如會員卡號、訂單號、學生號之類的,因為我們無法預測未來會不會因為業務需要,而出現業務字段重復或者重用的情況。
  • 通常情況下會用自增字段來做主鍵,對于單機系統來說是沒問題的。但是,如果有多臺服務器,各自都可以錄入數據,那就不一定適用了。因為如果每臺機器各自產生的數據需要合并,就可能會出現主鍵重復的問題,這時候就需要考慮分布式 id 的方案了。

#性別字段能加索引么?為啥?

不建議針對性別字段加索引。

實際上與索引創建規則之一區分度有關,性別字段假設有100w數據,50w男、50w女,區別度幾乎等于 0 。

區分度的計算方式 :select count(DISTINCT sex)/count(*) from sys_user

實際上對于性別字段不適合創建索引,是因為select * 操作,還得進行50w次回表操作,根據主鍵從聚簇索引中找到其他字段 ,這一部分開銷從上面的測試來說還是比較大的,所以從性能角度來看不建議性別字段加索引,加上索引并不是索引失效,而是回表操作使得變慢的。

既然走索引的查詢的成本比全表掃描高,優化器就會選擇全表掃描的方向進行查詢,這時候建立的性別字段索引就沒有啟到加快查詢的作用,反而還因為創建了索引占用了空間。

#表中十個字段,你主鍵用自增ID還是UUID,為什么?

用的是自增 id。

因為 uuid 相對順序的自增 id 來說是毫無規律可言的,新行的值不一定要比之前的主鍵的值要大,所以 innodb 無法做到總是把新行插入到索引的最后,而是需要為新行尋找新的合適的位置從而來分配新的空間。

這個過程需要做很多額外的操作,數據的毫無順序會導致數據分布散亂,將會導致以下的問題:

  • 寫入的目標頁很可能已經刷新到磁盤上并且從緩存上移除,或者還沒有被加載到緩存中,innodb 在插入之前不得不先找到并從磁盤讀取目標頁到內存中,這將導致大量的隨機 IO。
  • 因為寫入是亂序的,innodb 不得不頻繁的做頁分裂操作,以便為新的行分配空間,頁分裂導致移動大量的數據,影響性能。
  • 由于頻繁的頁分裂,頁會變得稀疏并被不規則的填充,最終會導致數據會有碎片。

結論:使用 InnoDB 應該盡可能的按主鍵的自增順序插入,并且盡可能使用單調的增加的聚簇鍵的值來插入新行。

#什么自增ID更快一些,UUID不快嗎,它在B+樹里面存儲是有序的嗎?

自增的主鍵的值是順序的,所以 Innodb 把每一條記錄都存儲在一條記錄的后面,所以自增 id 更快的原因:

  • 下一條記錄就會寫入新的頁中,一旦數據按照這種順序的方式加載,主鍵頁就會近乎于順序的記錄填滿,提升了頁面的最大填充率,不會有頁的浪費
  • 新插入的行一定會在原有的最大數據行下一行,mysql定位和尋址很快,不會為計算新行的位置而做出額外的消耗
  • 減少了頁分裂和碎片的產生

但是 UUID 不是遞增的,MySQL 中索引的數據結構是 B+Tree,這種數據結構的特點是索引樹上的節點的數據是有序的,而如果使用 UUID 作為主鍵,那么每次插入數據時,因為無法保證每次產生的 UUID 有序,所以就會出現新的 UUID 需要插入到索引樹的中間去,這樣可能會頻繁地導致頁分裂,使性能下降。

而且,UUID 太占用內存。每個 UUID 由 36 個字符組成,在字符串進行比較時,需要從前往后比較,字符串越長,性能越差。另外字符串越長,占用的內存越大,由于頁的大小是固定的,這樣一個頁上能存放的關鍵字數量就會越少,這樣最終就會導致索引樹的高度越大,在索引搜索的時候,發生的磁盤 IO 次數越多,性能越差。

#Mysql中的索引是怎么實現的 ?

MySQL InnoDB 引擎是用了B+樹作為了索引的數據結構。

B+Tree 是一種多叉樹,葉子節點才存放數據,非葉子節點只存放索引,而且每個節點里的數據是按主鍵順序存放的。每一層父節點的索引值都會出現在下層子節點的索引值中,因此在葉子節點中,包括了所有的索引值信息,并且每一個葉子節點都有兩個指針,分別指向下一個葉子節點和上一個葉子節點,形成一個雙向鏈表。

主鍵索引的 B+Tree 如圖所示:

img

比如,我們執行了下面這條查詢語句:

select * from product where id= 5;

這條語句使用了主鍵索引查詢 id 號為 5 的商品。查詢過程是這樣的,B+Tree 會自頂向下逐層進行查找:

  • 將 5 與根節點的索引數據 (1,10,20) 比較,5 在 1 和 10 之間,所以根據 B+Tree的搜索邏輯,找到第二層的索引數據 (1,4,7);
  • 在第二層的索引數據 (1,4,7)中進行查找,因為 5 在 4 和 7 之間,所以找到第三層的索引數據(4,5,6);
  • 在葉子節點的索引數據(4,5,6)中進行查找,然后我們找到了索引值為 5 的行數據。

數據庫的索引和數據都是存儲在硬盤的,我們可以把讀取一個節點當作一次磁盤 I/O 操作。那么上面的整個查詢過程一共經歷了 3 個節點,也就是進行了 3 次 I/O 操作。

B+Tree 存儲千萬級的數據只需要 3-4 層高度就可以滿足,這意味著從千萬級的表查詢目標數據最多需要 3-4 次磁盤 I/O,所以B+Tree 相比于 B 樹和二叉樹來說,最大的優勢在于查詢效率很高,因為即使在數據量很大的情況,查詢一個數據的磁盤 I/O 依然維持在 3-4次。

#查詢數據時,到了B+樹的葉子節點,之后的查找數據是如何做?

數據頁中的記錄按照「主鍵」順序組成單向鏈表,單向鏈表的特點就是插入、刪除非常方便,但是檢索效率不高,最差的情況下需要遍歷鏈表上的所有節點才能完成檢索。

因此,數據頁中有一個頁目錄,起到記錄的索引作用,就像我們書那樣,針對書中內容的每個章節設立了一個目錄,想看某個章節的時候,可以查看目錄,快速找到對應的章節的頁數,而數據頁中的頁目錄就是為了能快速找到記錄。那 InnoDB 是如何給記錄創建頁目錄的呢?

頁目錄與記錄的關系如下圖:

圖片

頁目錄創建的過程如下:

  1. 將所有的記錄劃分成幾個組,這些記錄包括最小記錄和最大記錄,但不包括標記為“已刪除”的記錄;
  2. 每個記錄組的最后一條記錄就是組內最大的那條記錄,并且最后一條記錄的頭信息中會存儲該組一共有多少條記錄,作為 n_owned 字段(上圖中粉紅色字段)
  3. 頁目錄用來存儲每組最后一條記錄的地址偏移量,這些地址偏移量會按照先后順序存儲起來,每組的地址偏移量也被稱之為槽(slot),每個槽相當于指針指向了不同組的最后一個記錄。

從圖可以看到,頁目錄就是由多個槽組成的,槽相當于分組記錄的索引。然后,因為記錄是按照「主鍵值」從小到大排序的,所以我們通過槽查找記錄時,可以使用二分法快速定位要查詢的記錄在哪個槽(哪個記錄分組),定位到槽后,再遍歷槽內的所有記錄,找到對應的記錄,無需從最小記錄開始遍歷整個頁中的記錄鏈表。以上面那張圖舉個例子,5 個槽的編號分別為 0,1,2,3,4,我想查找主鍵為 11 的用戶記錄:

  • 先二分得出槽中間位是 (0+4)/2=2 ,2號槽里最大的記錄為 8。因為 11 > 8,所以需要從 2 號槽后繼續搜索記錄;
  • 再使用二分搜索出 2 號和 4 槽的中間位是 (2+4)/2= 3,3 號槽里最大的記錄為 12。因為 11 < 12,所以主鍵為 11 的記錄在 3 號槽里;
  • 再從 3 號槽指向的主鍵值為 9 記錄開始向下搜索 2 次,定位到主鍵為 11 的記錄,取出該條記錄的信息即為我們想要查找的內容。

#B+樹的特性是什么?

  • 所有葉子節點都在同一層:這是B+樹的一個重要特性,確保了所有數據項的檢索都具有相同的I/O延遲,提高了搜索效率。每個葉子節點都包含指向相鄰葉子節點的指針,形成一個鏈表,由于葉子節點之間的鏈接,B+樹非常適合進行范圍查詢和排序掃描。可以沿著葉子節點的鏈表順序訪問數據,而無需進行多次隨機訪問。
  • 非葉子節點存儲鍵值:非葉子節點僅存儲鍵值和指向子節點的指針,不包含數據記錄。這些鍵值用于指導搜索路徑,幫助快速定位到正確的葉子節點。并且,由于非葉子節點只存放鍵值,當數據量比較大時,相對于B樹,B+樹的層高更少,查找效率也就更高。
  • 葉子節點存儲數據記錄:與B樹不同,B+樹的葉子節點存儲實際的數據記錄或指向數據記錄的指針。這意味著每次搜索都會到達葉子節點,才能找到所需數據。
  • 自平衡:B+樹在插入、刪除和更新操作后會自動重新平衡,確保樹的高度保持相對穩定,從而保持良好的搜索性能。每個節點最多可以有M個子節點,最少可以有ceil(M/2)個子節點(除了根節點),這里的M是樹的階數。

#說說B+樹和B樹的區別

  • 在B+樹中,數據都存儲在葉子節點上,而非葉子節點只存儲索引信息;而B樹的非葉子節點既存儲索引信息也存儲部分數據。
  • B+樹的葉子節點使用鏈表相連,便于范圍查詢和順序訪問;B樹的葉子節點沒有鏈表連接。
  • B+樹的查找性能更穩定,每次查找都需要查找到葉子節點;而B樹的查找可能會在非葉子節點找到數據,性能相對不穩定。

#B+樹的好處是什么?

B 樹和 B+ 都是通過多叉樹的方式,會將樹的高度變矮,所以這兩個數據結構非常適合檢索存于磁盤中的數據。

但是 MySQL 默認的存儲引擎 InnoDB 采用的是 B+ 作為索引的數據結構,原因有:

img

  • B+ 樹的非葉子節點不存放實際的記錄數據,僅存放索引,因此數據量相同的情況下,相比存儲即存索引又存記錄的 B 樹,B+樹的非葉子節點可以存放更多的索引,因此 B+ 樹可以比 B 樹更「矮胖」,查詢底層節點的磁盤 I/O次數會更少。
  • B+ 樹有大量的冗余節點(所有非葉子節點都是冗余索引),這些冗余索引讓 B+ 樹在插入、刪除的效率都更高,比如刪除根節點的時候,不會像 B 樹那樣會發生復雜的樹的變化;
  • B+ 樹葉子節點之間用鏈表連接了起來,有利于范圍查詢,而 B 樹要實現范圍查詢,因此只能通過樹的遍歷來完成范圍查詢,這會涉及多個節點的磁盤 I/O 操作,范圍查詢效率不如 B+ 樹。

#B+樹的葉子節點鏈表是單向還是雙向?

雙向的,為了實現倒序遍歷或者排序。

image-20240725232027951

Innodb 使用的 B+ 樹有一些特別的點,比如:

  • B+ 樹的葉子節點之間是用「雙向鏈表」進行連接,這樣的好處是既能向右遍歷,也能向左遍歷。
  • B+ 樹點節點內容是數據頁,數據頁里存放了用戶的記錄以及各種信息,每個數據頁默認大小是 16 KB。

Innodb 根據索引類型不同,分為聚集和二級索引。他們區別在于,聚集索引的葉子節點存放的是實際數據,所有完整的用戶記錄都存放在聚集索引的葉子節點,而二級索引的葉子節點存放的是主鍵值,而不是實際數據。

因為表的數據都是存放在聚集索引的葉子節點里,所以 InnoDB 存儲引擎一定會為表創建一個聚集索引,且由于數據在物理上只會保存一份,所以聚簇索引只能有一個,而二級索引可以創建多個。

#MySQL為什么用B+樹結構?和其他結構比的優點?

  • **B+Tree vs B Tree:**B+Tree 只在葉子節點存儲數據,而 B 樹 的非葉子節點也要存儲數據,所以 B+Tree 的單個節點的數據量更小,在相同的磁盤 I/O 次數下,就能查詢更多的節點。另外,B+Tree 葉子節點采用的是雙鏈表連接,適合 MySQL 中常見的基于范圍的順序查找,而 B 樹無法做到這一點。
  • **B+Tree vs 二叉樹:**對于有 N 個葉子節點的 B+Tree,其搜索復雜度為O(logdN),其中 d 表示節點允許的最大子節點個數為 d 個。在實際的應用當中, d 值是大于100的,這樣就保證了,即使數據達到千萬級別時,B+Tree 的高度依然維持在 3~4 層左右,也就是說一次數據查詢操作只需要做 3~4 次的磁盤 I/O 操作就能查詢到目標數據。而二叉樹的每個父節點的兒子節點個數只能是 2 個,意味著其搜索復雜度為 O(logN),這已經比 B+Tree 高出不少,因此二叉樹檢索到目標數據所經歷的磁盤 I/O 次數要更多。
  • **B+Tree vs Hash:**Hash 在做等值查詢的時候效率賊快,搜索復雜度為 O(1)。但是 Hash 表不適合做范圍查詢,它更適合做等值的查詢,這也是 B+Tree 索引要比 Hash 表索引有著更廣泛的適用場景的原因

#為什么 MysSQL 不用 跳表?

B+樹的高度在3層時存儲的數據可能已達千萬級別,但對于跳表而言同樣去維護千萬的數據量那么所造成的跳表層數過高而導致的磁盤io次數增多,也就是使用B+樹在存儲同樣的數據下磁盤io次數更少。

#聯合索引的實現原理?

將將多個字段組合成一個索引,該索引就被稱為聯合索引。

比如,將商品表中的 product_no 和 name 字段組合成聯合索引(product_no, name),創建聯合索引的方式如下:

CREATE INDEX index_product_no_name ON product(product_no, name);

聯合索引(product_no, name) 的 B+Tree 示意圖如下:

img

可以看到,聯合索引的非葉子節點用兩個字段的值作為 B+Tree 的 key 值。當在聯合索引查詢數據時,先按 product_no 字段比較,在 product_no 相同的情況下再按 name 字段比較。

也就是說,聯合索引查詢的 B+Tree 是先按 product_no 進行排序,然后再 product_no 相同的情況再按 name 字段排序。

因此,使用聯合索引時,存在最左匹配原則,也就是按照最左優先的方式進行索引的匹配。在使用聯合索引進行查詢的時候,如果不遵循「最左匹配原則」,聯合索引會失效,這樣就無法利用到索引快速查詢的特性了。

比如,如果創建了一個 (a, b, c) 聯合索引,如果查詢條件是以下這幾種,就可以匹配上聯合索引:

  • where a=1;
  • where a=1 and b=2 and c=3;
  • where a=1 and b=2;

需要注意的是,因為有查詢優化器,所以 a 字段在 where 子句的順序并不重要。

但是,如果查詢條件是以下這幾種,因為不符合最左匹配原則,所以就無法匹配上聯合索引,聯合索引就會失效:

  • where b=2;
  • where c=3;
  • where b=2 and c=3;

上面這些查詢條件之所以會失效,是因為(a, b, c) 聯合索引,是先按 a 排序,在 a 相同的情況再按 b 排序,在 b 相同的情況再按 c 排序。所以,b 和 c 是全局無序,局部相對有序的,這樣在沒有遵循最左匹配原則的情況下,是無法利用到索引的。

我這里舉聯合索引(a,b)的例子,該聯合索引的 B+ Tree 如下:

img

可以看到,a 是全局有序的(1, 2, 2, 3, 4, 5, 6, 7 ,8),而 b 是全局是無序的(12,7,8,2,3,8,10,5,2)。因此,直接執行where b = 2這種查詢條件沒有辦法利用聯合索引的,利用索引的前提是索引里的 key 是有序的

只有在 a 相同的情況才,b 才是有序的,比如 a 等于 2 的時候,b 的值為(7,8),這時就是有序的,這個有序狀態是局部的,因此,執行where a = 2 and b = 7是 a 和 b 字段能用到聯合索引的,也就是聯合索引生效了。

#創建聯合索引時需要注意什么?

建立聯合索引時的字段順序,對索引效率也有很大影響。越靠前的字段被用于索引過濾的概率越高,實際開發工作中建立聯合索引時,要把區分度大的字段排在前面,這樣區分度大的字段越有可能被更多的 SQL 使用到

區分度就是某個字段 column 不同值的個數「除以」表的總行數,計算公式如下:

img

比如,性別的區分度就很小,不適合建立索引或不適合排在聯合索引列的靠前的位置,而 UUID 這類字段就比較適合做索引或排在聯合索引列的靠前的位置。

因為如果索引的區分度很小,假設字段的值分布均勻,那么無論搜索哪個值都可能得到一半的數據。在這些情況下,還不如不要索引,因為 MySQL 還有一個查詢優化器,查詢優化器發現某個值出現在表的數據行中的百分比(慣用的百分比界線是"30%")很高的時候,它一般會忽略索引,進行全表掃描。

#聯合索引ABC,現在有個執行語句是A = XXX and C < XXX,索引怎么走

根據最左匹配原則,A可以走聯合索引,C不會走聯合索引,但是C可以走索引下推

#聯合索引(a,b,c) ,查詢條件 where b > xxx and a = x 會生效嗎

索引會生效,a 和 b 字段都能利用聯合索引,符合聯合索引最左匹配原則。

#聯合索引 (a, b,c),where條件是 a=2 and c = 1,能用到聯合索引嗎?

會用到聯合索引,但是只有 a 才能走索引,c 無法走索引,因為不符合最左匹配原則。雖然 c 無法走索引, 但是 c 字段在 5.6 版本之后,會有索引下推的優化,能減少回表查詢的次數。

#索引失效有哪些?

6 種會發生索引失效的情況:

  • 當我們使用左或者左右模糊匹配的時候,也就是 like %xx 或者 like %xx%這兩種方式都會造成索引失效;
  • 當我們在查詢條件中對索引列使用函數,就會導致索引失效。
  • 當我們在查詢條件中對索引列進行表達式計算,也是無法走索引的。
  • MySQL 在遇到字符串和數字比較的時候,會自動把字符串轉為數字,然后再進行比較。如果字符串是索引列,而條件語句中的輸入參數是數字的話,那么索引列會發生隱式類型轉換,由于隱式類型轉換是通過 CAST 函數實現的,等同于對索引列使用了函數,所以就會導致索引失效。
  • 聯合索引要能正確使用需要遵循最左匹配原則,也就是按照最左優先的方式進行索引的匹配,否則就會導致索引失效。
  • 在 WHERE 子句中,如果在 OR 前的條件列是索引列,而在 OR 后的條件列不是索引列,那么索引會失效。

#什么情況下會回表查詢

從物理存儲的角度來看,索引分為聚簇索引(主鍵索引)、二級索引(輔助索引)。

它們的主要區別如下:

  • 主鍵索引的 B+Tree 的葉子節點存放的是實際數據,所有完整的用戶記錄都存放在主鍵索引的 B+Tree 的葉子節點里;
  • 二級索引的 B+Tree 的葉子節點存放的是主鍵值,而不是實際數據。

所以,在查詢時使用了二級索引,如果查詢的數據能在二級索引里查詢的到,那么就不需要回表,這個過程就是覆蓋索引。

如果查詢的數據不在二級索引里,就會先檢索二級索引,找到對應的葉子節點,獲取到主鍵值后,然后再檢索主鍵索引,就能查詢到數據了,這個過程就是回表

#什么是覆蓋索引?

覆蓋索引是指一個索引包含了查詢所需的所有列,因此不需要訪問表中的數據行就能完成查詢。

換句話說,查詢所需的所有數據都能從索引中直接獲取,而不需要進行回表查詢。覆蓋索引能夠顯著提高查詢性能,因為減少了訪問數據頁的次數,從而減少了I/O操作。

假設有一張表 employees,表結構如下:

CREATE TABLE employees (id INT PRIMARY KEY,name VARCHAR(100),age INT,department VARCHAR(100),salary DECIMAL(10, 2)
);CREATE INDEX idx_name_age_department ON employees(name, age, department);

如果我們有以下查詢:

SELECT name, age, department FROM employees WHERE name = 'John';

在這種情況下,idx_name_age_department 是一個覆蓋索引,因為它包含了查詢所需的所有列:name、age 和 department。查詢可以完全在索引層完成,而不需要訪問表中的數據行。

#如果一個列即使單列索引,又是聯合索引,單獨查它的話先走哪個?

mysql 優化器會分析每個索引的查詢成本,然后選擇成本最低的方案來執行 sql。

如果單列索引是 a,聯合索引是(a ,b),那么針對下面這個查詢:

select a, b from table where a = ? and b =?

優化器會選擇聯合索引,因為查詢成本更低,查詢也不需要回表,直接索引覆蓋了。

#索引已經建好了,那我再插入一條數據,索引會有哪些變化?

插入新數據可能導致B+樹結構的調整和索引信息的更新,以保持B+樹的平衡性和正確性,這些變化通常由數據庫系統自動處理,確保數據的一致性和索引的有效性。

如果插入的數據導致葉子節點已滿,可能會觸發葉子節點的分裂操作,以保持B+樹的平衡性。

#索引字段是不是建的越多越好?

不是,建的的越多會占用越多的空間,而且在寫入頻繁的場景下,對于B+樹的維護所付出的性能消耗也會越大

#如果有一個字段是status值為0或者1,適合建索引嗎

不適合,區分度低的字段不適合建立索引。

#索引的優缺點?

索引最大的好處是提高查詢速度,但是索引也是有缺點的,比如:

  • 需要占用物理空間,數量越大,占用空間越大;
  • 創建索引和維護索引要耗費時間,這種時間隨著數據量的增加而增大;
  • 會降低表的增刪改的效率,因為每次增刪改索引,B+ 樹為了維護索引有序性,都需要進行動態維護。

所以,索引不是萬能鑰匙,它也是根據場景來使用的。

#怎么決定建立哪些索引?

什么時候適用索引?

  • 字段有唯一性限制的,比如商品編碼;
  • 經常用于?WHERE?查詢條件的字段,這樣能夠提高整個表的查詢速度,如果查詢條件不是一個字段,可以建立聯合索引。
  • 經常用于?GROUP BY?和?ORDER BY?的字段,這樣在查詢的時候就不需要再去做一次排序了,因為我們都已經知道了建立索引之后在 B+Tree 中的記錄都是排序好的。

什么時候不需要創建索引?

  • WHERE?條件,GROUP BYORDER BY?里用不到的字段,索引的價值是快速定位,如果起不到定位的字段通常是不需要創建索引的,因為索引是會占用物理空間的。
  • 字段中存在大量重復數據,不需要創建索引,比如性別字段,只有男女,如果數據庫表中,男女的記錄分布均勻,那么無論搜索哪個值都可能得到一半的數據。在這些情況下,還不如不要索引,因為 MySQL 還有一個查詢優化器,查詢優化器發現某個值出現在表的數據行中的百分比很高的時候,它一般會忽略索引,進行全表掃描。
  • 表數據太少的時候,不需要創建索引;
  • 經常更新的字段不用創建索引,比如不要對電商項目的用戶余額建立索引,因為索引字段頻繁修改,由

#索引優化詳細講講

常見優化索引的方法:

  • 前綴索引優化:使用前綴索引是為了減小索引字段大小,可以增加一個索引頁中存儲的索引值,有效提高索引的查詢速度。在一些大字符串的字段作為索引時,使用前綴索引可以幫助我們減小索引項的大小。
  • 覆蓋索引優化:覆蓋索引是指 SQL 中 query 的所有字段,在索引 B+Tree 的葉子節點上都能找得到的那些索引,從二級索引中查詢得到記錄,而不需要通過聚簇索引查詢獲得,可以避免回表的操作。
  • 主鍵索引最好是自增的:
    • 如果我們使用自增主鍵,那么每次插入的新數據就會按順序添加到當前索引節點的位置,不需要移動已有的數據,當頁面寫滿,就會自動開辟一個新頁面。因為每次插入一條新記錄,都是追加操作,不需要重新移動數據,因此這種插入數據的方法效率非常高。
    • 如果我們使用非自增主鍵,由于每次插入主鍵的索引值都是隨機的,因此每次插入新的數據時,就可能會插入到現有數據頁中間的某個位置,這將不得不移動其它數據來滿足新數據的插入,甚至需要從一個頁面復制數據到另外一個頁面,我們通常將這種情況稱為頁分裂。頁分裂還有可能會造成大量的內存碎片,導致索引結構不緊湊,從而影響查詢效率。
  • 防止索引失效:
    • 當我們使用左或者左右模糊匹配的時候,也就是?like %xx?或者?like %xx%這兩種方式都會造成索引失效;
    • 當我們在查詢條件中對索引列做了計算、函數、類型轉換操作,這些情況下都會造成索引失效;
    • 聯合索引要能正確使用需要遵循最左匹配原則,也就是按照最左優先的方式進行索引的匹配,否則就會導致索引失效。
    • 在 WHERE 子句中,如果在 OR 前的條件列是索引列,而在 OR 后的條件列不是索引列,那么索引會失效。

#了解過前綴索引嗎?

使用前綴索引是為了減小索引字段大小,可以增加一個索引頁中存儲的索引值,有效提高索引的查詢速度。在一些大字符串的字段作為索引時,使用前綴索引可以幫助我們減小索引項的大小。

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

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

相關文章

低代碼平臺的版本管理深度解析

引言 在當今快速發展的軟件開發領域&#xff0c;低代碼平臺憑借其可視化界面和拖拽功能&#xff0c;極大地減少了手動編碼的工作量&#xff0c;顯著提高了開發效率和質量。它提供了豐富的預構建模塊、組件和服務&#xff0c;讓開發者能夠根據業務需求和邏輯進行組合與配置&…

Springboot項目由JDK8升級至JDK17全過程教程【文末附源碼】

1. 前言 最近一直想把我的開源項目maple-boot升級到jdk17版本&#xff0c;然后接入Spring AI。拖延癥犯了一直拖拖拖&#xff0c;最近時間空閑較多&#xff0c;開始陸續著手升級。 整個升級過程 計劃分為3步。 step1&#xff1a;先將項目升級到jdk17&#xff0c;使用正常ste…

同步與異步:軟件工程中的時空藝術與實踐智慧-以蜻蜓hr人才系統舉例-優雅草卓伊凡

同步與異步&#xff1a;軟件工程中的時空藝術與實踐智慧-以蜻蜓hr人才系統舉例-優雅草卓伊凡 概念解析&#xff1a;時空維度的編程范式 在軟件開發的宇宙中&#xff0c;同步(Synchronous)與異步(Asynchronous)是兩種根本不同的執行模式&#xff0c;它們塑造了程序與時間和空間…

TF-IDF算法的代碼實踐應用——關鍵詞提取、文本分類、信息檢索

回顧&#xff1a;TF-IDF算法詳解與實踐總結 上一篇文章我們深入剖析了TF-IDF的原理與細節&#xff0c;但實踐才是檢驗真理的唯一標準&#xff01;今天&#xff0c;我們將從“紙上談兵”轉向“實戰演練”&#xff1a;通過純Python手寫實現與調用sklearn工具包兩種方式&#xff0…

前端面試寶典---事件循環面試題

瀏覽器進程模型與 JavaScript 執行機制 現代瀏覽器采用多進程架構&#xff0c;包含瀏覽器進程、渲染進程、網絡進程等多個核心進程。每個標簽頁會獨立創建一個渲染進程&#xff0c;負責頁面內容的解析、渲染和執行腳本代碼。 JavaScript 的單線程特性 JavaScript 采用單線程…

postman調用接口報錯401, Unauthorized, Invalid Token. null解決辦法

1、先登錄系統&#xff0c;F12找到token并復制 2、postman里選中Authorization,下拉選中選擇Bearer Token,把復制好的token黏貼到右側輸入框&#xff0c;如下所示&#xff1a; 3、如果是json格式的參數拷貝到Body中&#xff0c;如下所示&#xff1a; 4、 接口調用成功

C++----剖析list

前面學習了vector和string&#xff0c;接下來剖析stl中的list&#xff0c;在數據庫中學習過&#xff0c;list邏輯上是連續的&#xff0c;但是存儲中是分散的&#xff0c;這是與vector這種數組類型不同的地方。所以list中的元素設置為一個結構體&#xff0c;將list設計成雙向的&…

為什么已經有 Nginx 了,還需要服務網關?

在當前微服務架構中&#xff0c;雖然 Nginx 是一個高性能的反向代理和負載均衡服務器&#xff0c;但在實際使用中仍然存在諸多局限性。為了滿足運維效率、功能統一治理以及與微服務生態集成的需求&#xff0c;通常會在 Nginx 和業務服務之間引入一層基于 Java 實現的服務網關&a…

Kendo UI 中,ViewModel、DataSource 和 Grid的關系。Kendo 框架發起 HTTP 請求

Kendo UI 中&#xff0c;ViewModel、DataSource 和 Grid的關系 在 Kendo UI 中&#xff0c;ViewModel、DataSource 和 Grid 是構建動態數據應用的核心組件&#xff0c;三者協同工作實現數據的綁定、管理和展示。 一、三者關系圖解 #mermaid-svg-3lWxu2zWB23wDYEz {font-family…

宇樹開源 Qmini 雙足機器人,可通過 3D 打印動手制作,使用樹莓派作為主控制器

Unitree Qmini 是一款由宇樹科技設計并開源的低成本雙足機器人&#xff0c;開發者可以完全通過 3D 打印進行復刻。Qmini 專為業余愛好者、教育工作者和研究人員設計&#xff0c;使用戶能夠快速上手&#xff0c;并以類似樂高的模塊化方式組裝自己的機器人。該項目為機器人技術提…

解決華為云服務器無法ping通github問題

在push代碼到github上的時候&#xff0c;發現顯示22端口無法連接&#xff0c;在已經開放了端口&#xff0c;防火墻關閉的情況下仍然無法連接到GitHub。 發現是服務器和github斷連&#xff0c;選擇 sudo vim /etc/hosts 添加一下代碼 # GitHub Start140.82.121.4 gith…

關于electron-vite koffi 讀取 dll 打包等問題得記錄

koffi const koffi require(‘koffi’) import iconv from ‘iconv-lite’;const libPath path.resolve(__dirname, ‘…/…/resources/dll/sss.dll’) const yktLib koffi.load(libPath) const ret yktLib.func(‘string sss(string Url, string Data, string OutData)’…

【開發技術】.Net使用FFmpeg視頻特定幀上繪制內容

目錄 一、目的 二、解決方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg調用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 濾鏡來繪制 ROI 三、總結 一、目的 當前市場上有很多目標檢測智能識別的相關算法&#xff0c;當前調用一個醫療行業的AI識別算法后返回…

通過關鍵字批量抓取淘寶商品數據實現方法途徑分享--API

item_search 按關鍵字搜索淘寶商品item_search_tmall 按關鍵字搜索天貓商品item_search_pro 高級關鍵字搜索淘寶商品item_search_img 按圖搜索淘寶商品&#xff08;拍立淘&#xff09;item_search_shop 獲得店鋪的所有商品 一、引言 在電商領域&#xff0c;獲取淘寶商品數據對…

用 Lazarus IDE 寫一個郵件客戶端軟件,能收發郵件,編寫郵件

下面是一個使用Lazarus IDE開發的基本郵件客戶端實現方案&#xff0c;包含收發郵件和編寫郵件的核心功能。我們將使用Synapse庫&#xff08;跨平臺的網絡通信庫&#xff09;來處理郵件協議。 步驟1&#xff1a;安裝依賴 安裝Synapse庫&#xff1a; 下載地址&#xff1a;https:…

第二部分-IP及子網劃分

目錄 一、什么是IP? 1.1.IP地址的由來 1.2.IP地址的表示 1.3.IP地址的構成 1.4.IP地址的分類 1.5.IP地址類型 1.6.IP地址的計算 1.7.私網IP地址 1.8.特殊IP地址 二、子網劃分 2.1.什么是子網劃分及為什么要進行子網劃分? 2.2.如何進行子網劃分&#xff1f; 實例&#xff1a; …

【javascript】泡泡龍游戲中反彈和查找匹配算法

引言 泡泡龍游戲的核心玩法依賴于物理碰撞與顏色匹配的算法實現。反彈效果需要模擬泡泡與邊界或障礙物的彈性碰撞&#xff0c;確保軌跡符合物理規律&#xff1b;匹配算法則需快速檢測相鄰同色泡泡&#xff0c;觸發消除邏輯。高效的處理方式直接影響游戲流暢度和玩家體驗。 以…

如何使用deepseek滿血版

deepseek 訪問方式 DeepSeek滿血版可通過官方網站或官方應用商店下載安裝。確保設備滿足最低系統要求&#xff0c;如操作系統版本和硬件配置。 賬號注冊與登錄 訪問平臺后完成賬號注冊流程&#xff0c;提供必要信息并驗證郵箱或手機號。登錄后進入用戶中心&#xff0c;查看…

網絡管理【Linux/Unix/Windows】命令大全

在跨平臺網絡運維中&#xff0c;管理員常需快速切換Windows與Linux環境下的命令操作。本文整合了核心網絡管理命令的跨平臺對照表&#xff0c;涵蓋連通性測試、路由追蹤、DNS解析、ARP管理、會話監控等高頻場景。無論您負責服務器維護、網絡排障還是安全審計&#xff0c;此表可…

Gremlin創建schema(包括實體和關系)

1、構建圖譜schema&#xff0c;流程包括圖創建、實體構建以及關系構建。 創建圖時需要指定圖庫名稱以及主鍵字段。 實體構建時需要指定主鍵字段&#xff0c;每個屬性需要指定數據類型&#xff0c;是否非空以及默認值。關系構建時需要包括關系名稱、指向頭實體的標簽&#xff0c…