系列文章
一、原來一條select語句在MySQL是這樣執(zhí)行的《死磕MySQL系列 一》
二、一生摯友redo log、binlog《死磕MySQL系列 二》
三、MySQL強(qiáng)人“鎖”難《死磕MySQL系列 三》
看過前幾期文章的伙伴會發(fā)現(xiàn)并沒有聊過關(guān)于索引和事務(wù)的知識點,這兩個大點再之前的文章中已經(jīng)寫過了。
這里給大家一個傳送門點擊直接查看哈!
接下來打開普通索引和唯一索引的世界。
一、了解普通索引和唯一索引
普通索引
MySQL中基本索引類型,沒有什么限制,允許在定義索引的列中插入重復(fù)值和空值,純粹為了查詢數(shù)據(jù)更快一點。
唯一索引
索引列中的值必須是唯一的,但是允許為空值。
主鍵索引是一種特殊的唯一索引,不允許有空值。
擴(kuò)展一下其它兩中索引,知識點放在一起記憶會更好
全文索引
只能在char,varchar,text類型字段上使用全文索引,介紹了要求,說說什么是全文索引,就是在一堆文字中,通過其中的某個關(guān)鍵字等,就能找到該字段所屬的記錄行,比如有“你是個靚仔,靚女。。。”通過靚仔,可能就可以找到該條記錄。
空間索引
空間索引是對空間數(shù)據(jù)類型的字段建立的索引,MySQL中的空間數(shù)據(jù)類型有四種,GEOMETRY、POINT、LINESTRING、POLYGON。在創(chuàng)建空間索引時,使用SPATIAL關(guān)鍵字。要求,引擎為Myisam,創(chuàng)建空間索引的列,必須將其聲明為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)
二、應(yīng)用場景
現(xiàn)在你應(yīng)該知道普通索引和唯一索引的區(qū)別,接下來看看在一些場景下如何選擇兩個索引。
丁老師文章中提到一個業(yè)務(wù)場景是市民系統(tǒng),通過身份證號來查姓名。
這里咔咔也借用這個場景來給大家通過咔咔的思路描述一下這個流程。
執(zhí)行語句為select name from user where card = '6104301996xxxxxxxx';
這個場景第一反應(yīng)肯定是給card創(chuàng)建一個索引,但創(chuàng)建什么索引呢?主鍵索引肯定不建議使用。
思考:為什么不能用身份證號來作為主鍵索引?
三、為什么不能用太大的值作為主鍵
Innodb存儲引擎的主鍵索引結(jié)構(gòu)如下圖
普通索引數(shù)據(jù)結(jié)構(gòu)如下圖
主鍵索引的葉子節(jié)點存儲的是對應(yīng)主鍵的整行數(shù)據(jù)。
普通索引的葉子節(jié)點存儲的是對應(yīng)的主鍵值。
如果說B+Tree讀取數(shù)據(jù)的深度是三層,每個磁盤的大小為16kb。
那在B+Tree中非葉子節(jié)點可以存儲多少數(shù)據(jù)呢!一般來說我們每個表都會存在一個主鍵。
根據(jù)三層來計算,第一層跟第二層存儲的是key值,也就是主鍵值。
都知道int類型所占的內(nèi)存時4Byte(字節(jié)),指針的存儲就給個6Byte,一共就是10Tybe,那么第一層節(jié)點就可以存儲16 * 1000 /10 = 1600。
同理第二層每個節(jié)點也是可以存儲1600個key。
第三層是葉子節(jié)點,每個磁盤存儲大小同樣安裝BTree的計算一樣,每條數(shù)據(jù)占1kb。
在B+Tree中三層可以存儲的數(shù)據(jù)就是1600 * 1600 * 16 = 40960000
結(jié)論:若主鍵過大會直接影響索引存儲的數(shù)據(jù)量,所以非常不建議使用過大的數(shù)據(jù)作為主鍵索引。
四、從查詢的角度分析
假設(shè)現(xiàn)在要查card = 5 這條記錄,查詢過程為,先通過B+樹從樹根開始,按層搜索到葉子節(jié)點,然后通過二分法來定位card = 5 的這條記錄。
普通索引
對于普通索引來說當(dāng)找到card = 5這條記錄后,還會繼續(xù)查找,直到碰到第一個不滿足card = 5的記錄為止。
唯一索引
對于唯一索引就非常簡單的了,唯一索引的特性就是數(shù)據(jù)唯一性,所以查到card = 5這條記錄后就不在查找下一條記錄了。
普通索引多查詢的一次對性能影響大嗎?
這個影響幾乎可以忽略,在之前的幾期文章中咔咔給大家普及了一個名詞“局部性原理
”。
數(shù)據(jù)和程序都有聚集成群的傾向,在訪問了一條數(shù)據(jù)之后,在之后有極大的可能再次訪問這條數(shù)據(jù)和這條數(shù)據(jù)的相鄰數(shù)據(jù)。
所以說MySQL的Innodb存儲引擎,在讀取數(shù)據(jù)時也會采取這種局部性原理,每次讀取的數(shù)據(jù)是16kb,也就是一頁。
在Innodb存儲引擎下每頁的大小默認(rèn)為16kb,這個參數(shù)也可以進(jìn)行調(diào)整,參數(shù)為innodb_page_size。
但有一種情況雖說幾率非常低,但還是需要知道的。
當(dāng)索引為普通索引時,查到的數(shù)據(jù)正好是一頁的最后一個數(shù)據(jù),此時就需要讀取下一頁的數(shù)據(jù),這個操作是有點復(fù)雜,但對于現(xiàn)在的CPU來說可以忽略不計。
五、了解change buffer
首先,需要先了解一個新的知識點change buffer。
當(dāng)需要更新card = 5這條記錄時,這條數(shù)據(jù)所在的數(shù)據(jù)頁在內(nèi)存中就直接更新,如若不在的話就需要將更新的操作緩存在change buffer中。當(dāng)下次查詢需要訪問這個數(shù)據(jù)頁時,將這個數(shù)據(jù)頁讀入內(nèi)存,然后執(zhí)行change buffer中與這個頁有關(guān)的操作。
接著,了解另一個新的知識點merge。
當(dāng)把change buffer中的數(shù)據(jù)應(yīng)用到數(shù)據(jù)頁,得到最新結(jié)果的過程成為merge,另外數(shù)據(jù)庫正常關(guān)閉的過程中,也會執(zhí)行merge操作。
結(jié)論:更新操作將記錄先記錄到change buffer中,可以減少磁盤I/O,語句執(zhí)行速度會提升。
注意
1、 數(shù)據(jù)從change buffer讀入內(nèi)存是需要占用buffer pool的,使用change buffer可以避免占用內(nèi)存。
2、change buffer 也是可以持久化數(shù)據(jù)的,change buffer 在內(nèi)存中有拷貝,也會被寫入到磁盤。
六、change buffer在什么條件下使用
思考:為什么唯一索引使用不到change buffer
唯一索引肯定是用不到,對于這個答案如果你感覺有點不適,就需要在回到之前幾期文章再好好看看。
唯一索引插入一行數(shù)據(jù)時都會執(zhí)行一次查詢操作判斷表中是否已經(jīng)存在這條記錄,判斷是否違反唯一約束,既然必須得把數(shù)據(jù)頁的數(shù)據(jù)讀入內(nèi)存,那還用change buffer個什么勁啊!
因此,只有普通索引可以使用。
在上文中知道了將change buffer數(shù)據(jù)讀入內(nèi)存時是需要占用buffer pool的內(nèi)存,因此在MySQL中也給了一個參數(shù)來設(shè)置change buffer的大小。跟其它的數(shù)據(jù)單位可能有點出入,若設(shè)置為30,就表示change buffer只占用buffer pool內(nèi)存的30%。
思考:在什么場景下不能使用change buffer?
change buffer的作用是將更新的動作緩存下來,所以對一個數(shù)據(jù)頁做merge時,change buffer記錄的變更越多,收益就越大。
但也并不是所有場景都適用,咔咔目前所開發(fā)的是一款賬款軟件,大部分更新后都是立馬查看,這種情況是不是就違背了上面說的對一個數(shù)據(jù)頁做merge時,change buffer記錄的越多,收益越大。
因此,只有寫多讀少的場景,change buffer才能發(fā)揮非常大的作用。
思考:為什么更新完立馬查詢change buffer就沒多大用處了呢?
一條記錄發(fā)起更新操作后,先記錄到change buffer 中,接著,當(dāng)查詢的數(shù)據(jù)在這個數(shù)據(jù)頁時會立即觸發(fā)merge,這樣隨機(jī)訪問的IO的次數(shù)不會減少,反而增加了change buffer的維護(hù)代價。所以說這種業(yè)務(wù)模式使用change biffer會起到反作用。
思考:如何關(guān)閉change buffer
只需要將參數(shù)innodb_change_buffer_max_size = 0 即可。
七、從更新語句性能的影響的角度分析
第一種情況這條數(shù)據(jù)要更新的數(shù)據(jù)頁在內(nèi)存中。
唯一索引:在內(nèi)存中查找是否有這條記錄,不存在時則插入這個值。
普通索引:直接更新需要更新的值即可。
結(jié)論: 當(dāng)要更新的數(shù)據(jù)頁在內(nèi)存中時,唯一索引就比普通索引多一次判斷。
第二種情況這條數(shù)據(jù)要更新的數(shù)據(jù)頁不在內(nèi)存中。
唯一索引:需要將這條數(shù)據(jù)所在的數(shù)據(jù)頁讀入內(nèi)存中,查找是否存在這條記錄,然后更新數(shù)據(jù)。
普通索引:將這條要更新的數(shù)據(jù)記錄在change buffer即可。
結(jié)論: change buffer 當(dāng)更新的數(shù)據(jù)不在數(shù)據(jù)頁中時,如果你的索引是普通索引則可以很顯著的提升性能。
注意: 當(dāng)你把一個索引從普通索引改為唯一索引時一定要注意change buffer的影響,會直接影響內(nèi)存命中率。
八、總結(jié)
回到文章主題如何選擇普通索引和唯一索引,在查詢方面兩者是沒有什么差別的,主要是在更新操作上的影響。
如果你的業(yè)務(wù)跟咔咔的場景一樣,更新后立馬要對這個記錄查詢,那么就可以選擇直接關(guān)閉change buffer。
若不是這種場景,則盡量選擇普通索引,使用change buffer可以非常明顯的提升更新性能。
“
堅持學(xué)習(xí)、堅持寫作、堅持分享是咔咔從業(yè)以來所秉持的信念。愿文章在偌大的互聯(lián)網(wǎng)上能給你帶來一點幫助,我是咔咔,下期見。
”