關於索引必須知道的知識

mysql索引的各類概念

在學習索引的時候,經常會看到回表、覆蓋索引、索引下推、頁分裂等等概念,本篇就常見概念進行介紹和總結,但願能幫助你們快速掌握這些「高大上」的概念。mysql

索引基於B+樹,要想更好地理解這些概念建議先了解談談MySQL索引底層實現之數據結構數據結構之B+樹算法

回表

根據葉子節點的內容, 索引類型分爲主鍵索引和非主鍵索引。(mysql索引的數據結構是B+樹,對這方面知識看不懂的能夠參考)sql

  • 主鍵索引的葉子節點存的是整行數據。 在InnoDB裏, 主鍵索引也被稱爲聚簇索引(clustered index)。
  • 非主鍵索引的葉子節點內容是索引字段值+主鍵的值。 在InnoDB裏, 非主鍵索引也被稱爲二級索引(secondary index) 。

基於主鍵索引和普通索引的查詢有什麼區別?性能優化

普通索引查詢方式,則須要先搜索索引樹,獲得主鍵值,再到主鍵索引樹按主鍵值搜索一次,這個過程稱爲回表數據結構

也就是說,基於非主鍵索引的查詢須要多掃描一棵索引樹。 所以, 咱們在應用中應該儘可能使用主鍵查詢。函數

索引維護——頁分裂

什麼是頁分裂?性能

若是R5所在的數據頁已經滿了, 根據B+樹的算法, 這時候須要申請一個新的數據頁,而後挪動部分數據過去。這個過程稱爲頁分裂。 固然有分裂就有合併。當相鄰兩個頁因爲刪除了數據,利用率很低以後, 會將數據頁作合併。 合併的過程, 能夠認爲是分裂過程的逆過程mysql索引

頁分裂的壞處學習

除了性能外, 頁分裂操做還影響數據頁的利用率。本來放在一個頁的數據, 如今分到兩個頁中,總體空間利用率下降大約50%。優化

如何避免頁分裂?

使用自增主鍵。每次插入一條新記錄, 都是追加操做,都不涉及到挪動其餘記錄,也不會觸發葉子節點的分裂。

自增主鍵的其餘好處

因爲每一個非主鍵索引的葉子節點上都是主鍵的值。,若是用身份證號作主鍵, 那麼每一個二級索引的葉子節點佔用約20個字節, 而若是用整型作主鍵, 則只要4個字節, 若是是長整型(bigint)則是8個字節。顯然, 主鍵長度越小, 普通索引的葉子節點就越小, 普通索引佔用的空間也就越小。

有沒有什麼場景適合用業務字段直接作主鍵的呢?

好比,有些業務的場景需求是這樣的:

  1. 只有一個索引
  2. 該索引必須是惟一索引

因爲沒有其餘索引, 因此也就不用考慮其餘索引的葉子節點大小的問題。這時候咱們就要優先考慮上一段提到的「儘可能使用主鍵查詢」原則, 直接將這個索引設置爲主鍵,能夠避免每次查詢須要搜索兩棵樹。

覆蓋索引

非彙集索引的B+樹節點存儲的是索引列和主鍵,假如想要拿到完整數據的話還得根據主鍵去主鍵索引樹回表,這樣性能很差,若是咱們要查詢獲得的數據就是索引列和主鍵中的數據,就不要回表。這樣只須要在一棵索引樹上就能獲取SQL所需的全部列數據無需回表的索引稱爲覆蓋索引

因爲覆蓋索引能夠減小樹的搜索次數, 顯著提高查詢性能, 因此使用覆蓋索引是一個經常使用的性能優化手段。

最左匹配原則

B+樹這種索引結構, 能夠利用索引的「最左前綴」來定位記錄。

索引樹排序規則:在對聯合索引創建索引樹時,會按照索引字段的順序依次排序。以(name,age,address)這個聯合索引爲例,首先按照name排序完,在name排序值相同時繼續按照age排序。

最左匹配:在mysql創建聯合索引時還會遵循最左前綴匹配的原則,即最左優先,在檢索數據時從聯合索引的最左邊開始匹配(左邊的匹配不上,後面也不會再去匹配了)。同時,索引只能用於查找key是否存在(相等),遇到範圍查詢 (>、<、between、like左匹配)等就不能進一步匹配了,後續退化爲線性查找。所以,列的排列順序決定了可命中索引的列數

在創建聯合索引的時候, 如何安排索引內的字段順序?

  • 考慮索引的複用能力。 由於能夠支持最左前綴, 因此當已經有了(a,b)這個聯合索引後, 通常就不須要單獨在a上創建索引了。 所以, 第一原則是, 若是經過調整順序, 能夠少維護一個索引, 那麼這個順序每每就是須要優先考慮採用的。
  • 考慮空間。不要無節制的建立索引。

前綴索引

對字符串的前幾個字符(具體是幾個字符在創建索引時指定)創建索引,這樣創建起來的索引佔用空間更小

對字符串創建普通索引和前綴索引的語句以下:

# 普通字符串索引
alter table SUser add index index1(email);
# 前綴索引,索引長度爲6
alter table SUser add index index2(email(6));

前綴索引的優點和損失

優點:佔用的空間會更小

損失:會增長額外的記錄掃描次數

使用前綴索引,定義好長度, 就能夠作到既節省空間,又不用額外增長太多的查詢成本。

當要給字符串建立前綴索引時如何肯定應該使用多長的前綴?

前綴索引確定會損失區分度,咱們須要提早預設一個能夠接受的損失比列,使用count計算出多種長度的損失比例,選擇低於損失比例的最短長度。

第一步:計算算出這個列上有多少個不一樣的值:

select count(distinct email) as L from SUser

第二步:計算不一樣長度去重後有多少數據:

select
	count(distinct left(email,4)) as L4,
	count(distinct left(email,5)) as L5,
	count(distinct left(email,6)) as L6,
	count(distinct left(email,7)) as L7,
from SUser;

第三步:選擇合適的長度

在返回的L4~L7中,找出不小於 L * 95%的值,假設這裏L六、L7都知足,你就能夠選擇前綴長度爲6。

前綴索引對覆蓋索引的影響

使用前綴索引就不能使用覆蓋索引對查詢性能進行優化了。由於索引只包含了字符串的部分數據。

遇到前綴的區分度不夠好的狀況時,咱們要怎麼辦?

  1. 使用倒序存儲:
    • 不會消耗額外的字段,可是每次索引通常不止4個字符,索引樹須要多的存儲空間
    • 每次寫和讀的時候,都須要額外調用一次reverse函數
    • 查詢時有前綴索引的問題:會增長額外的記錄掃描次數
    • 不支持範圍查找
  2. 使用hash字段
    • 須要額外添加一個hash字段
    • 每次須要額外調用一次crc32()函數
    • 查詢性能相對倒序存儲更穩定一些
    • 不支持範圍查找
    • 再也不是使用前綴索引的方式

索引下推

上一段咱們說到知足最左前綴原則的時候,最左前綴能夠用於在索引中定位記錄。 這時,你可能要問,那些不符合最左前綴的部分, 會怎麼樣呢?

mysql> select * from tuser where name like '張%' and age=10 and ismale=1;

咱們仍是以市民表的聯合索引(name, age) 爲例。 若是如今有一個需求: 檢索出表中「名字第一個字是張, 並且年齡是10歲的全部男孩」。 那麼, SQL語句是這麼寫的:

你已經知道了前綴索引規則, 因此這個語句在搜索索引樹的時候, 只能用 「張」, 找到第一個知足條件的記錄ID3。 固然, 這還不錯, 總比全表掃描要好。而後呢?固然是判斷其餘條件是否知足。

在MySQL 5.6以前, 只能從ID3開始一個個回表。 到主鍵索引上找出數據行, 再對比字段值。

而MySQL 5.6 引入的索引下推優化(indexcondition pushdown), 能夠在索引遍歷過程當中, 對索引中包含的字段(age)先作判斷, 直接過濾掉不知足條件的記錄, 減小回表次數。

本文記錄比較零散,若是有模棱兩可或者不對的地方歡迎你們指正。

相關文章
相關標籤/搜索