如何選擇普通索引和唯一索引《死磕MySQL系列 五》

發表於2021-11-03

系列文章

一、原來一條select語句在MySQL是這樣執行的《死磕MySQL系列 一》

二、一生摯友redo log、binlog《死磕MySQL系列 二》

三、MySQL強人“鎖”難《死磕MySQL系列 三》

四、S 鎖與 X 鎖的愛恨情仇《死磕MySQL系列 四》

看過前幾期文章的夥伴會發現並沒有聊過關於索引和事務的知識點,這兩個大點再之前的文章中已經寫過了。

這裡給大家一個傳送門點選直接檢視哈!

揭開MySQL索引神祕面紗

上來就問MySQL事務,瑟瑟發抖...

MVCC:聽說有人好奇我的底層實現

幻讀:聽說有人認為我是被MVCC幹掉的

接下來開啟普通索引和唯一索引的世界。

一、瞭解普通索引和唯一索引

普通索引

MySQL中基本索引型別,沒有什麼限制,允許在定義索引的列中插入重複值和空值,純粹為了查詢資料更快一點。

唯一索引

索引列中的值必須是唯一的,但是允許為空值。

主鍵索引是一種特殊的唯一索引,不允許有空值。

擴充套件一下其它兩中索引,知識點放在一起記憶會更好

全文索引

只能在char,varchar,text型別欄位上使用全文索引,介紹了要求,說說什麼是全文索引,就是在一堆文字中,通過其中的某個關鍵字等,就能找到該欄位所屬的記錄行,比如有“你是個靚仔,靚女。。。”通過靚仔,可能就可以找到該條記錄。

空間索引

空間索引是對空間資料型別的欄位建立的索引,MySQL中的空間資料型別有四種,GEOMETRY、POINT、LINESTRING、POLYGON。在建立空間索引時,使用SPATIAL關鍵字。要求,引擎為Myisam,建立空間索引的列,必須將其宣告為not null。

索引新增方式

1、 主鍵索引:alter table table_name add primary key (column)

2、 唯一索引:alter table table_name add unique (column)

3、普通索引:alter table table_name add index index_name (column)

4、全文索引:alter table table_name add fulltext (column)

5、多列索引:alter table table_name add index index_name (column1,column2,column3)

二、應用場景

現在你應該知道普通索引和唯一索引的區別,接下來看看在一些場景下如何選擇兩個索引。

丁老師文章中提到一個業務場景是市民系統,通過身份證號來查姓名。

這裡咔咔也借用這個場景來給大家通過咔咔的思路描述一下這個流程。

執行語句為select name from user where card = '6104301996xxxxxxxx';

這個場景第一反應肯定是給card建立一個索引,但建立什麼索引呢?主鍵索引肯定不建議使用。

思考:為什麼不能用身份證號來作為主鍵索引?

三、為什麼不能用太大的值作為主鍵

Innodb儲存引擎的主鍵索引結構如下圖

普通索引資料結構如下圖

主鍵索引的葉子節點儲存的是對應主鍵的整行資料。

普通索引的葉子節點儲存的是對應的主鍵值。

如果說B+Tree讀取資料的深度是三層,每個磁碟的大小為16kb。

那在B+Tree中非葉子節點可以儲存多少資料呢!一般來說我們每個表都會存在一個主鍵。

根據三層來計算,第一層跟第二層儲存的是key值,也就是主鍵值。

都知道int型別所佔的記憶體時4Byte(位元組),指標的儲存就給個6Byte,一共就是10Tybe,那麼第一層節點就可以儲存16 * 1000 /10 = 1600。

同理第二層每個節點也是可以儲存1600個key。

第三層是葉子節點,每個磁碟儲存大小同樣安裝BTree的計算一樣,每條資料佔1kb。

在B+Tree中三層可以儲存的資料就是1600 * 1600 * 16 = 40960000

結論:若主鍵過大會直接影響索引儲存的資料量,所以非常不建議使用過大的資料作為主鍵索引。

四、從查詢的角度分析

假設現在要查card = 5 這條記錄,查詢過程為,先通過B+樹從樹根開始,按層搜尋到葉子節點,然後通過二分法來定位card = 5 的這條記錄。

普通索引

對於普通索引來說當找到card = 5這條記錄後,還會繼續查詢,直到碰到第一個不滿足card = 5的記錄為止。

唯一索引

對於唯一索引就非常簡單的了,唯一索引的特性就是資料唯一性,所以查到card = 5這條記錄後就不在查詢下一條記錄了。

普通索引多查詢的一次對效能影響大嗎?

這個影響幾乎可以忽略,在之前的幾期文章中咔咔給大家普及了一個名詞“區域性性原理”。

資料和程式都有聚整合群的傾向,在訪問了一條資料之後,在之後有極大的可能再次訪問這條資料和這條資料的相鄰資料。

所以說MySQL的Innodb儲存引擎,在讀取資料時也會採取這種區域性性原理,每次讀取的資料是16kb,也就是一頁。

在Innodb儲存引擎下每頁的大小預設為16kb,這個引數也可以進行調整,引數為innodb_page_size。

但有一種情況雖說機率非常低,但還是需要知道的。

當索引為普通索引時,查到的資料正好是一頁的最後一個資料,此時就需要讀取下一頁的資料,這個操作是有點複雜,但對於現在的CPU來說可以忽略不計。

五、瞭解change buffer

首先,需要先了解一個新的知識點change buffer。

當需要更新card = 5這條記錄時,這條資料所在的資料頁在記憶體中就直接更新,如若不在的話就需要將更新的操作快取在change buffer中。當下次查詢需要訪問這個資料頁時,將這個資料頁讀入記憶體,然後執行change buffer中與這個頁有關的操作。

接著,瞭解另一個新的知識點merge。

當把change buffer中的資料應用到資料頁,得到最新結果的過程成為merge,另外資料庫正常關閉的過程中,也會執行merge操作。

結論:更新操作將記錄先記錄到change buffer中,可以減少磁碟I/O,語句執行速度會提升。

注意

1、 資料從change buffer讀入記憶體是需要佔用buffer pool的,使用change buffer可以避免佔用記憶體。

2、change buffer 也是可以持久化資料的,change buffer 在記憶體中有拷貝,也會被寫入到磁碟。

六、change buffer在什麼條件下使用

思考:為什麼唯一索引使用不到change buffer

唯一索引肯定是用不到,對於這個答案如果你感覺有點不適,就需要在回到之前幾期文章再好好看看。

唯一索引插入一行資料時都會執行一次查詢操作判斷表中是否已經存在這條記錄,判斷是否違反唯一約束,既然必須得把資料頁的資料讀入記憶體,那還用change buffer個什麼勁啊!

因此,只有普通索引可以使用。

在上文中知道了將change buffer資料讀入記憶體時是需要佔用buffer pool的記憶體,因此在MySQL中也給了一個引數來設定change buffer的大小。跟其它的資料單位可能有點出入,若設定為30,就表示change buffer只佔用buffer pool記憶體的30%。

思考:在什麼場景下不能使用change buffer?

change buffer的作用是將更新的動作快取下來,所以對一個資料頁做merge時,change buffer記錄的變更越多,收益就越大。

但也並不是所有場景都適用,咔咔目前所開發的是一款賬款軟體,大部分更新後都是立馬檢視,這種情況是不是就違背了上面說的對一個資料頁做merge時,change buffer記錄的越多,收益越大。

因此,只有寫多讀少的場景,change buffer才能發揮非常大的作用。

思考:為什麼更新完立馬查詢change buffer就沒多大用處了呢?

一條記錄發起更新操作後,先記錄到change buffer 中,接著,當查詢的資料在這個資料頁時會立即觸發merge,這樣隨機訪問的IO的次數不會減少,反而增加了change buffer的維護代價。所以說這種業務模式使用change biffer會起到反作用。

思考:如何關閉change buffer

只需要將引數innodb_change_buffer_max_size = 0 即可。

七、從更新語句效能的影響的角度分析

第一種情況這條資料要更新的資料頁在記憶體中。

唯一索引:在記憶體中查詢是否有這條記錄,不存在時則插入這個值。

普通索引:直接更新需要更新的值即可。

結論: 當要更新的資料頁在記憶體中時,唯一索引就比普通索引多一次判斷。

第二種情況這條資料要更新的資料頁不在記憶體中。

唯一索引:需要將這條資料所在的資料頁讀入記憶體中,查詢是否存在這條記錄,然後更新資料。

普通索引:將這條要更新的資料記錄在change buffer即可。

結論: change buffer 當更新的資料不在資料頁中時,如果你的索引是普通索引則可以很顯著的提升效能。

注意: 當你把一個索引從普通索引改為唯一索引時一定要注意change buffer的影響,會直接影響記憶體命中率。

八、總結

回到文章主題如何選擇普通索引和唯一索引,在查詢方面兩者是沒有什麼差別的,主要是在更新操作上的影響。

如果你的業務跟咔咔的場景一樣,更新後立馬要對這個記錄查詢,那麼就可以選擇直接關閉change buffer。

若不是這種場景,則儘量選擇普通索引,使用change buffer可以非常明顯的提升更新效能。

堅持學習、堅持寫作、堅持分享是咔咔從業以來所秉持的信念。願文章在偌大的網際網路上能給你帶來一點幫助,我是咔咔,下期見。

相關文章