注:以下所有內(nèi)容均為自己總結(jié)的筆記,涉及底層原理,難度對標(biāo)18K-25K薪資,偏理論,不保證百分百準(zhǔn)確性。
索引查找快速的原理?
創(chuàng)建索引的本質(zhì)是排序,排好序之后再找數(shù)據(jù)就快了。
對于B+tree索引,B+tree對數(shù)據(jù)排序后采用多路查找思想的非線性查找方案,減少了大量的查詢次數(shù),從而避免多次磁盤io,進(jìn)而快速找到結(jié)果。
為什么推薦用自增id做主鍵?
自增id直觀,且不用刻意維護(hù)這個字段,減少工作量,還能避免主鍵更新引起的頁分裂。
舉例說明頁分裂:數(shù)據(jù)是存在頁上的,頁1存儲id為1、2、5的數(shù)據(jù),如果沒有設(shè)置自增,如果突然新增了id為3、4的數(shù)據(jù),頁1無剩余空間存儲,就需要將頁1數(shù)據(jù)進(jìn)行拆分,頁1存儲id為1、2、3的數(shù)據(jù),頁2存儲id為4、5的數(shù)據(jù),分裂的目的是為了排序,排序的目的是為了方便查找。分裂需要消耗計算資源用于更改數(shù)據(jù),這種非必要發(fā)生的操作就盡量避免。
什么是鏈表 ,在索引中起到了什么作用?
鏈表是一種線性數(shù)據(jù)結(jié)構(gòu),由節(jié)點組成,每個節(jié)點包含兩部分:數(shù)據(jù)和指向相鄰節(jié)點的指針。
鏈表分單向和雙向。
單向:節(jié)點只有一個指針,指向下一個節(jié)點
雙向:每個節(jié)點有兩個指針,一個指向前一個節(jié)點,一個指向后一個節(jié)點。
根據(jù)對鏈表的操作,又可以分為隊列和棧。
隊列:先進(jìn)先出(LPush->RPop,或Ppush->Rpop)。
棧:先進(jìn)后出(LPush->LPop,或RPush->RPop)。
MySQL InnoDB引擎和MyISAM引擎,都用的B+tree算法作索引,在葉子節(jié)點,每個節(jié)點間使用向左或者向右的指針,來移動指針,這也是索引支持區(qū)間查詢的原因,葉子節(jié)點間組成一個鏈表。
什么是B+tree?
是一種通過排序,方便查找的數(shù)據(jù)結(jié)構(gòu),特別是在數(shù)據(jù)庫和文件系統(tǒng)的實現(xiàn)中廣泛應(yīng)用。它是一種平衡樹的變體。
平衡性: 所有葉子節(jié)點都位于同一層,使得在樹的高度方面達(dá)到平衡,從而保持高效的查找、插入和刪除操作。
有序性: B+樹的葉子節(jié)點按照鍵值大小順序存儲,使得范圍查詢變得更為高效。
多路搜索: 每個非葉子節(jié)點都有多個子節(jié)點,允許更多的分支,提高搜索效率。
適用于范圍查詢: 由于有序性和多路搜索的特性,B+樹在范圍查詢方面表現(xiàn)優(yōu)秀。
在數(shù)據(jù)庫系統(tǒng)中,B+樹常被用作索引結(jié)構(gòu),用于加速對數(shù)據(jù)庫表的查詢操作。其設(shè)計考慮了磁盤存儲的特性,使得在磁盤上的讀寫操作更為高效。
B+tree比二叉樹好在那里?
更加平衡:二叉樹在遇到一組非混亂的數(shù)據(jù)集合下,樹的層級會變的很高,意味著io的次數(shù)變多,B+tree避開了這個問題。
多路搜索:二叉樹為雙路搜索,B+tree為多路搜索,極大提高了搜索效率。
查找方便:無論是區(qū)間查找還是定值查找,B+tree都比二叉樹查找方便,二叉樹需要中序遍歷才能得到有序序列。
Btree與B+tree區(qū)別?
Btree:非葉子節(jié)點存放數(shù)據(jù),葉子節(jié)點無指針,支持區(qū)間查找。
B+tree:非葉子結(jié)點不存放數(shù)據(jù),葉子節(jié)點有指針,支持更快速的區(qū)間查找。
正因為Btree非葉子節(jié)點存放數(shù)據(jù),查詢起來無法像B+tree一樣葉子節(jié)點間依靠鏈表進(jìn)行范圍查詢,所以區(qū)間查詢效率低。
Btree做查找,需要在葉子節(jié)點和非葉子節(jié)點之間來回跳躍搜索,來回的跳躍,意味著需要更多的磁盤io,而B+tree只需要從非葉子節(jié)點到葉子節(jié)點即可,
所以穩(wěn)定性不如B+tree。
為什么MySQL采用B+tree?
查詢高效:因為B+tree采用多路非線性查找思想,降低樹的層級,減少磁盤io。
支持區(qū)間查詢:因為數(shù)據(jù)全在葉子節(jié)點上,每個節(jié)點之間有指針做關(guān)聯(lián)。
算法穩(wěn)定:不會向二叉搜索樹那樣,層級和數(shù)據(jù)有關(guān),查找情況時好時壞。
頁根頁之間怎么關(guān)聯(lián)?
雙向鏈表。
雙向鏈表因為有序,所以可以適用二分查找。
雙向鏈表,鏈的是行中存儲的元數(shù)據(jù),這歸行格式管理,行格式存儲的其中一項叫做record_type,有4個值,0表示普通用戶記錄,1表示目錄項紀(jì)錄,2表示當(dāng)前頁的最小值,3表示當(dāng)前頁的最大值。
使用0和1區(qū)分是目錄頁還是數(shù)據(jù)頁。使用2和3走索引時用于定位,進(jìn)行區(qū)間或等值查找。
為什么數(shù)據(jù)在頁上,頁本身要加索引?
雖然一個頁可以存儲多條數(shù)據(jù),但是在大數(shù)據(jù)情況下,一個頁不夠存就需要多個頁,為了避免查找數(shù)據(jù)時數(shù)據(jù)不用對大量的頁挨個遍歷。與是也加上了B+tree用于查找。
B+tree 葉子節(jié)點之間怎么關(guān)聯(lián)
雙向的指針。
什么是聚簇索引?
在InnoDB引擎中,索引的葉子節(jié)點存儲的是實際的數(shù)據(jù)行,數(shù)據(jù)即索引,索引即數(shù)據(jù)。
好處就是能帶來快速的查詢速度,通過索引就可以找到實實在在的數(shù)據(jù)。
一般一個表只能有一個聚簇索引,一般為主鍵,因為數(shù)據(jù)的排列就是按照索引來的,如果一個表中有多個聚簇索引,一是不知道二級索引參考哪個,二是太占空間。
MyISAM有聚簇索引嗎?
沒有。
查看MyISAM的二進(jìn)制文件,有.MYI(存儲索引)和.MYD(存儲數(shù)據(jù))后綴結(jié)尾的文件,他們的索引和數(shù)據(jù)是分開的,不符合數(shù)據(jù)即索引,索引即數(shù)據(jù)的特點。
MyISAM的葉子結(jié)點存儲的是數(shù)據(jù)的位置信息。
MyISAM中B+tree的葉子節(jié)點存儲的是數(shù)據(jù)的地址,也需要類似回表的操作,為什么性能也不慢?
因為尋址的性能也挺高的,如果速度慢,就不會這么主流了。
為什么InnoDB比MyISAM有更好的并發(fā)性能,是因為索引上有什么不同之處嗎?
不是,并發(fā)性和不同引擎的索引沒有太多相關(guān)性。
InnoDB有更好的并發(fā)性能,是因為它支持粒度更小的行級鎖,并發(fā)情況下,事務(wù)用于保持?jǐn)?shù)據(jù)一致性,鎖是并發(fā)控制必備的機制。
為什么InnoDB不推薦用較長的數(shù)據(jù)做主鍵?
大數(shù)據(jù)情況下,InnoDB引擎創(chuàng)建的二級B+tree索引,葉子節(jié)點是主鍵,較長的主鍵,會占用更多的位置。
而MyISAM中B+tree的葉子節(jié)點,存儲的是數(shù)據(jù)的位置。
如何區(qū)分一棵B+tree是不是聚簇索引?
看這顆樹的葉子節(jié)點上,存儲的是實實在在的數(shù)據(jù),還是根據(jù)當(dāng)前列關(guān)聯(lián)的主鍵。
聚簇索引的優(yōu)點?
把實實在在的數(shù)據(jù)當(dāng)索引,不用回表,性能很高,因為通過索引找到的那條數(shù)據(jù),就是所在行的數(shù)據(jù)。
InnoDB引擎,MySQL 默認(rèn)情況下使用自增主鍵作為聚簇索引,這便是主鍵查詢快的原因。
聚簇索引的缺點?
如果插入的數(shù)據(jù)不是自增的數(shù)字id,可能引起索引分裂,降低性能。
占用較大的空間。
為什么主鍵查詢性能很高?
InnoDB引擎,MySQL 默認(rèn)情況下使用自增主鍵作為聚簇索引,不用回表。這便是主鍵查詢快的原因。
具有唯一索引,定值查找,查到后不必接著找。
為什么不建議用UUID作為主鍵?
避免索引分裂,影響插入數(shù)據(jù)時的性能問題。
必須要明白,索引的本質(zhì)是排序,索引查找的本質(zhì)是根據(jù)排好序的數(shù)據(jù)進(jìn)行查找。
可能后生成的uuid,根據(jù)ASCII碼字典排序,會排到先生成uuid的前面,插入新值,則需要重新排序,就要破壞掉原本的索引結(jié)構(gòu),這個過程將消耗時間和算力。
c3dc38e1-8db2-4e9f-9fe4-735e88facdb4,像是這種類型的數(shù)據(jù)叫做uuid。
一般有兩個好處:
- 在分布式環(huán)境下保證唯一性,因為夠長且重復(fù)概率太低,否則,A模塊的id=1,可能會與B模塊的id=1混淆。
- 黑客不容易猜到相鄰的uuid是什么,就算程序有越權(quán)漏洞,也不會很難根據(jù)原ID猜測其它ID。
其次是uuid,占用空間比int類型更大,使得其它二級索引,存儲主鍵時,占用更多的空間。
用自增id就不會出現(xiàn)索引分裂的情況嗎?
不是的。
自增的有序id,只會減少插入數(shù)據(jù)時的分裂,當(dāng)大數(shù)據(jù)時的新增引起的B+tree分層,或者對數(shù)據(jù)的的插入和刪除操作,都可能為了局部重建索引,觸發(fā)分裂操作。
什么是非聚簇索引?
非聚簇索引則將索引與實際數(shù)據(jù)行分開存儲,索引的葉子節(jié)點存儲的是當(dāng)前索引值與主鍵(MyISAM則是當(dāng)前索引值與行地址),不是所在行的數(shù)據(jù)。
什么是二級索引?
級索引通常指的是除了表的主鍵之外創(chuàng)建的額外索引。
什么是輔助索引?
一般是指非聚簇索引或者二級索引。
什么是回表?
InnoDB引擎,在非聚簇的B+tree索引上,樹的葉子節(jié)點存儲的是當(dāng)前索引字段數(shù)據(jù)和主鍵的值,當(dāng)前的葉子節(jié)點數(shù)據(jù)與主鍵做邏輯上的關(guān)聯(lián),而不是存儲所在行的全部數(shù)據(jù),所以需要根據(jù)主鍵,再次查詢一遍數(shù)據(jù),這個過程叫回表。
因內(nèi)部多了一輪的查詢流程,所以性能有所降低,所以能用主鍵查詢的場景,就不要使用其它字段。
為什么不通過像聚簇索引一樣的方式避免回表?
技術(shù)上能實現(xiàn),但是缺點很明顯,空間換時間的代價太大。
在非聚簇的B+tree上,樹的葉子節(jié)點重復(fù)存儲所在行的值,會造成大量的空間浪費。
其次是更新代價太大,可能更新一小塊數(shù)據(jù),就需要對這些索引上的數(shù)據(jù)做同步更新。
所以做邏輯關(guān)聯(lián)更好。
為什么插入、更新、刪除數(shù)據(jù)時,非聚簇索引比聚簇索引性能略高?
插入數(shù)據(jù)引起的索引分裂問題,非聚簇索引只需要調(diào)整當(dāng)前索引的位置和主鍵就好,而非聚簇所以需要移動整行的數(shù)據(jù)。
什么是聯(lián)合索引?
組合索引、復(fù)合索引、多列索引,聯(lián)合索引一個意思,多個字段組合去創(chuàng)建索引。
索引排序規(guī)則:先按照左邊的字段進(jìn)行排序,如果左邊字段相同,再根據(jù)右邊的字段排序。
什么是前綴索引?
MySQL 前綴索引是一種,它只對列值的前部進(jìn)行索引,而不是對整個列值進(jìn)行索引。
做法:alter table 表名 add index (filed_name(長度));
優(yōu)點:主要用于控制索引大小,由于底層在比較時字符串長度較短,所以比較起來也比單列索引塊。
缺點:有誤差,所以需要在空間和產(chǎn)品服務(wù)方面做取舍。
評估設(shè)置長度:需要根據(jù)數(shù)據(jù)情況測試,如果count(distince left('字段名', 10)) / count(*) 等于1左右,說明截取字段前10個字符去重后的數(shù)量,等于總數(shù),說明依照前10個字符就能辨識度很高。如果結(jié)果略小于1,說明辨識度還不夠,可以取12,如果遠(yuǎn)小于1,長度取20再試試,以此類推。
補充:《阿里巴巴Java開發(fā)手冊》【強制】在varchar簡歷索引時,必須指定索引長度,沒必要對全字段建立索引,根據(jù)實際文本區(qū)分度決定索引長度。
聯(lián)合索引算不算聚簇索引?
相似但不算,聯(lián)合索引關(guān)聯(lián)的不是所在行的全部字段,而是部分字段。
為什么單表數(shù)據(jù)不能超過2000萬條
這是個粗略的理論值,很多人說超過這個數(shù),會把B+tree的層級轉(zhuǎn)為4層,其實不準(zhǔn)的。
這個還是要看葉子節(jié)點數(shù)據(jù)的大小,如果葉子結(jié)點很大,需要更多的頁,則存不了太多,如果葉子節(jié)點數(shù)據(jù)很少,有人推算,存1個億也沒問題的。
先排除一些元數(shù)據(jù)的存儲:數(shù)據(jù)存儲在頁上,每頁大小16KB,每頁需要開辟一些新的空間來存儲元數(shù)據(jù)(例如指向上一頁下一頁的指針),頁頭存儲文件頭38字節(jié),頁面頭56字節(jié),最小記錄和最大記錄26個字節(jié),為了保證不出錯,出現(xiàn)了校驗和的機制,這塊功能的存儲被放到了頁尾,占8個字節(jié)。頁里的數(shù)據(jù)呢,為了方便查找每行的數(shù)據(jù),所以包含頁目錄(采用二分法,把查詢復(fù)雜度從O(n)優(yōu)化為O(log n)),這也占空間,這些可以粗略的估計為占用了1KB。
聲明代數(shù):假設(shè)非葉子節(jié)點指向葉子節(jié)點的指針數(shù)量為X,葉子節(jié)點能夠容納的行數(shù)為Y,B+tree層數(shù)為Z,那么能存儲的總行數(shù)就是Xz-1 * Y。
計算X:主鍵假設(shè)用bigint,占8個字節(jié),頁號這個元數(shù)據(jù)占4個字節(jié),非葉子節(jié)點一條數(shù)據(jù)占12個字節(jié),15KB / 12B = 1280。
計算Y:假設(shè)一個行數(shù)據(jù)為1KB,也就是說可以放15條數(shù)據(jù)。
若Z為1:12800 * 15 = 15行
若Z為2:12801 * 15 = 19200行
若Z為3:12802 * 15 = 24576000行
若Z為3:12803 * 15 = 31457280000行
但是這是理想情況,很多主鍵id都用無符號int,能節(jié)省4個字節(jié),行數(shù)大小也不確定,所以這是個理論值,究竟是多少,需要根據(jù)實際情況討論。
什么是最左匹配原則?
生效的情況:
abc創(chuàng)建聯(lián)合索引,where a = 'v1' and b = 'v2' and c= 'v3',where順序可以顛倒,但是必須都是and,左邊的列不能包含區(qū)間查詢。
失效的情況:
多字段創(chuàng)建聯(lián)合索引,如果聯(lián)合索引左邊的字段的查詢條件不存在,或者聯(lián)合索引左邊字段使用的區(qū)間查詢,或者使用了or,都會導(dǎo)致索引失效。
注意,這里說的順序,是聯(lián)合索引的順序,不是where條件的順序。
底層什么原因?qū)е伦钭笃ヅ湓瓌t?
B+tree聯(lián)合索引排序,是根據(jù)ASCII碼的字典順序進(jìn)行從左到右依字符排序,然后依字段從左到右排序,沒有其它方向的排序,這就不能兼容更多種的查詢方式。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and b = v2 and c = v3,此時字段索引的使用情況?
每個字段都能用上索引。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 or b = v2 or c = v3,此時字段索引的使用情況?
a能用上索引,b和c都無法使用索引,因為and是屬于流水線式的篩選,而or是與前面的搜索條件不相關(guān)的個體,b和c都沒有左邊的字段配合成為聯(lián)合索引。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and b > v2 and c = v3,此時字段索引的使用情況?
a能用上索引,b能用上索引,c無法使用上索引,因為b是區(qū)間查詢導(dǎo)致c無法按索引查詢。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and c = v3,此時字段索引的使用情況?
a可以用上索引,c用不上索引,因為缺少b。
假如abc三個字段創(chuàng)建聯(lián)合索引,where b = v1 and c = v3,此時字段索引的使用情況?
b和c都用不上索引,因為缺少a。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and b = v2 and c = v3 or c is null,此時字段索引的使用情況?
abc三個字段全部能用上索引。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and b = v2 and c = v3 or d + 1 = 10,此時字段索引的使用情況?
用explain實測,type為all,索引用不上了,全表查,因為使用了表達(dá)式。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and b = v2 and c = v3 and d + 1 = 10,此時字段索引的使用情況?
用explain實測,type為ref,并根據(jù)key_len字段評估,abc都能使用索引。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and b = v2 and c = v3 and length(d) < 20,此時字段索引的使用情況?
用explain實測,type為ref,并根據(jù)key_len字段評估,abc都能使用索引。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and b = v2 and c = v3 or length(d) < 20,此時字段索引的使用情況?
用explain實測,type為all,索引用不上了,全表查,因為使用了函數(shù)。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and b = v2 and c = v3 or c = v4,此時字段索引的使用情況?
用explain實測,type為all,索引用不上了,全表查,因為c列沒有建索引。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a = v1 and b = v2 and c = v3 and c = v4,此時字段索引的使用情況?
用explain實測,type為ref,并根據(jù)key_len字段評估,abc都能使用索引。
假如abc三個字段創(chuàng)建聯(lián)合索引,where a > v1 and b = v2 and c = v3,索引失效,explain type為all,數(shù)據(jù)量大還經(jīng)常查詢,怎么辦?
創(chuàng)建bca的聯(lián)合索引即可,alter table 表名 add index(b,c,a),每個字段,最好加上索引長度。
abc的聯(lián)合索引,如果用不上,刪掉。alter table 表名 drop index 索引名。
為什么不推薦mysql頻繁使用null值?
null值是個特殊的存在,在sql查詢上,即使是唯一索引列,也允許插入多個null值,這影響了了唯一索引的唯一性約束。
其次有些查詢,用where field = '',或者where filed = null,都是匹配補上的,只能用is null。
null值影響聚合函數(shù)的使用,導(dǎo)致count(字段)結(jié)果不符合真實情況。
否則就難以區(qū)分到底是沒有關(guān)聯(lián)記錄還是其他情況。
什么是索引下推?
索引下推簡稱ICP,優(yōu)化SQL執(zhí)行的一種策略,將where條件下推至存儲引擎執(zhí)行,減少回表的數(shù)據(jù)量提高性能。
一般是針對二級索引說的,有多個where條件時,執(zhí)行完第一個條件不著急回表,用剩余的數(shù)據(jù)再次執(zhí)行第二個where條件,減少回表的數(shù)據(jù)量。
索引下推常見在聯(lián)合索引中,只有使用了聯(lián)合索引中的字段的時候,才可以。
舉例:百萬條數(shù)據(jù),第一次where之后剩下1000條,執(zhí)行完第二次where后身下5條,只需要回表5條數(shù)據(jù)即可,避免第一次where條件剩下的1000條數(shù)據(jù)回表,然后在執(zhí)行第二個where,再回表。
為什么有覆蓋索引時,不支持索引下推?
使用了覆蓋索引,說明索引的數(shù)據(jù)滿足了當(dāng)前select查詢,不需要回表,已經(jīng)不需要下推了。
索引下推有個暗含的前提,是索引無法完全滿足當(dāng)前查詢前提的優(yōu)化策略,且where的字段又包含在聯(lián)合索引中,索引下推的終極目的是減少回表數(shù)據(jù)數(shù)量,既然不需要回表,那就不需要下推。
為什么相關(guān)子查詢,不支持索引下推?
相關(guān)子查詢,指的是子sql與父sql的參數(shù)動態(tài)關(guān)聯(lián),這會導(dǎo)致子SQL語句的參數(shù)處于動態(tài)(不確定)狀態(tài),導(dǎo)致索引下推的目標(biāo)都無法確定,所以不行。
什么是mysql filesort?
這是mysql explain中Extra中可能會展示的東西,當(dāng)然也是一種機制,在order by的場景中去用。
這種方式指的是在內(nèi)存中排序,效率略低,因為沒有按照索引排序,盡量避免。
與之相對的,有個index排序,可以按照索引自然而然的排序,效率偏高。
filesort兩種排序算法是什么?
雙路排序:相對較慢。先找到orderby的列,然后排序,再根據(jù)排序的字段查找其它字段,類似回表,所以慢。其次相對于單路排序更可能發(fā)生隨機io,order by排序后的數(shù)據(jù),可能不在同一個頁上,這個過程需要來回的讀取頁中的數(shù)據(jù)。
單路排序:相對較快,根據(jù)orderby的列,一次性取出來所有字段,然后再排序。
什么是覆蓋索引?
要查詢的字段上有索引,索引中的字段涵蓋了select字段的結(jié)果,因為不需要回表操作去查詢整行數(shù)據(jù),避免回表的隨機io(回表的數(shù)據(jù)可能不在同一個頁上),這是一種性能優(yōu)化的提現(xiàn)。
需要盡可能少select 字段的數(shù)量,避免使用select *。
什么情況下not in、is not null、<>、!=,左%能用上索引?
覆蓋索引。
要查詢的字段上有索引,不用回表,卸掉了一個重?fù)?dān),MySQL優(yōu)化器認(rèn)為這代價不大,所以選擇用索引。
這種情況下,explain type為index。
什么是Hash索引?
基于哈希表實現(xiàn)的索引結(jié)構(gòu),用于快速定位數(shù)據(jù)的存儲位置。在Hash索引中,索引鍵通過哈希算法計算得到一個哈希值,該哈希值指向存儲數(shù)據(jù)的具體位置,從而實現(xiàn)快速的查找和定位。
MyISAM和InnoDB都支持哈希索引嗎?
都不支持,只有Memory引擎支持。
并且不支持區(qū)間查找,所以索引主要依靠B+tree。
where條件的順序會影響使用索引嗎?
如果都是and,則不影響。
MySQL有個東西叫做優(yōu)化器,它會根據(jù)查詢的字段,篩選的字段,索引情況自動調(diào)整。
order by調(diào)整順序會影響結(jié)果。
where條件左邊的or遇見右邊的and,誰會先執(zhí)行?
or的優(yōu)先級比and更小,會先執(zhí)行右邊的and,再執(zhí)行左邊的or,所以要控制好。避免索引失效。
那些查詢適合創(chuàng)建索引?
- 需要唯一性約束兜底的字段。
- 經(jīng)常被查詢或者作為where條件的字段,=、>、<、<=、>=、in、between、like 右百分號
- 經(jīng)常group by或者order by的字段。
- delete或update被作為where條件的字段。
- distinct的字段。
- join on的連接字段需要加索引,但是需要類型一致,因為MySQL內(nèi)部有用函數(shù)做隱式轉(zhuǎn)換,用了函數(shù)就不適用索引。
- 區(qū)分度(不重復(fù)度)高的字段。
- 把搜索最頻繁的列,放在聯(lián)合索引的左側(cè),(受聯(lián)合索引的最左原則影響)。
那些查詢不適合創(chuàng)建索引?
- 數(shù)據(jù)量小,一個表,例如配置表,總類別表,可能最多幾十條記錄,創(chuàng)建不創(chuàng)建區(qū)別不大。
- 寫多讀少,數(shù)據(jù)的寫操作對索引字段的開銷比沒有索引要大,而且讀操作還少。
- 區(qū)分度低的字段,例如性別狀態(tài)等,這會導(dǎo)致線性查找,能提升搜索效率,但是不明顯,可加可不加。
- sql語句包含<>、!=、not in、is not null,無法使用索引,所以專門用作排除性查找的,不建議創(chuàng)建索引。
哪些情況下索引會失效?
- 使用not in、is not null、<>、!=、這種排除法時會導(dǎo)致索引失效,覆蓋索引除外。
- 最左匹配原則,左邊的字段缺少時會出現(xiàn),覆蓋索引除外。
- 最左匹配原則,左邊的字段有區(qū)間查詢,導(dǎo)致右邊的字段無法使用索引。
- like左邊或兩邊加百分號。
- 類型的隱式轉(zhuǎn)換,如varchar的字段,使用where varchar_field = 123,包括join表,用on連接的字段。
- where條件有函數(shù),或表達(dá)式。
- where語句包含or,or中存在非索引列。
- 大數(shù)據(jù)量對二級索引字段排序,如果select * 或者其它字段,這個過程涉及回表,可能無法使用索引,因為數(shù)據(jù)量大,走索引的每條數(shù)據(jù)都需要回表,代價會很大。
- order by字段,如果排序與索引順序不一致,則可能導(dǎo)致索引失效,如果order by的每個字段,都按照索引的順序,或者反順序,則仍舊會走索引。
如果一個表中的每個字段都加了單列索引,且每個字段都查詢條件的操作,哪個索引會被優(yōu)先使用?
這是個開放性的問題,不能一概而論,不過可根據(jù)SQL語句的執(zhí)行順序判斷,先執(zhí)行sql語句的某個部分,如果這個部分有索引,那就使用這個索引,其它索引在此查詢過程中用不上。
可使用explain參考。
B+tree和Hash索引的適用場景?
B+tree:等值查找,區(qū)間查找,批量等值查找,order by。
hash:等值查找。
為什么大數(shù)據(jù)情況下,使用select *,并對某個或某些二級索引列order by,會導(dǎo)致索引失效?
因為數(shù)據(jù)量大又使用select * ,會導(dǎo)致二級索引的排序需要回表,這個太耗資源。所以MySQL優(yōu)化器選擇了全表查。
親測加一個limit數(shù)據(jù)量不超過49000,就能解決這個問題,explain type由all變成了index。
為什么唯一索引比普通索引略快 ?
唯一索引找到數(shù)據(jù)就不往下找了。
一次查詢,表中多個索引都可用,MySQL優(yōu)化器只能選一個嗎?
多數(shù)情況下是,但是還有index_merge(explain type的值)的情況。使用了多個單列索引來執(zhí)行查詢。當(dāng)在查詢條件中存在多個列,且每個列都有單獨的索引時。
什么是自適應(yīng)哈希?
InnoDB不支持hash索引,但是提供了一個自適應(yīng)的哈希索引,屬于MySQL內(nèi)部的優(yōu)化機制。
某些數(shù)據(jù)被經(jīng)常訪問,滿足等值查詢的時候,就會將這個數(shù)據(jù)頁的地址存放到哈希表中,下次查詢的時候直接用。
MySQL5.7和8默認(rèn)都是開啟的狀態(tài),可使用select @@innodb_adaptive_hash_index來查看。
為什么很多人都在講索引就不得不提磁盤io?
因為最耗時的環(huán)節(jié)就在磁盤io上,索引就是為了減少磁盤io的次數(shù)。
字段被刪除時,索引的情況?
單列索引,字段被刪除時,同步刪除索引。
聯(lián)合索引,部分字段被刪除時,索引刪除,自動根據(jù)剩余字段重建索引。索引全部字段被刪除時,索引刪除。
什么是降序索引?
這是mysql8的新特性,創(chuàng)建索引時指定索引的排序方式為降序,CREATE INDEX idx_column_name ON table_name (column_name DESC);
對創(chuàng)建聯(lián)合索引的列,進(jìn)行不同的排序順序時,使用降序排序,會提高性能。
前提是,需要保證創(chuàng)建索引的升降序與排序的升降序一致。
為什么子查詢比join慢?
子查詢的過程創(chuàng)建了臨時表,臨時表的創(chuàng)建和銷毀會占用時間。
而join的過程,會產(chǎn)生一個結(jié)果集,這個結(jié)果集不是臨時表。
count(*)、count(1)、count(字段),count(id)怎么選,哪個性能高?
如果想統(tǒng)計全部數(shù)據(jù),不推薦用count(字段),因為遇到null值不會+1,如果統(tǒng)計某字段數(shù)量,用count(字段)。
性能方面,同一個引擎下差不多。
不同引擎下,MyISAM比InnoDB引擎性能高,MyISAM中,表的元數(shù)據(jù)就存了count值,通過表級鎖自動維護(hù)一致性。時間復(fù)雜度為O(1)。InnoDB采用行級鎖和MVCC機制(事務(wù)相關(guān)分為當(dāng)前讀和快照讀之分,不同的事務(wù)隔離級別和讀出的數(shù)據(jù)不一致,會造成不準(zhǔn)確的情況),無法采用MyISAM的方案,所以要全表掃描,時間復(fù)雜度O(n)。
sql執(zhí)行順序where優(yōu)先于limit,在不加索引的列使用where,為什么大數(shù)據(jù)情況下加limit 1能顯著提升性能?
sql的執(zhí)行順序是:form->join on->where->group by->having->select->order by->limit,確實where在limit前面。
但是MySQL Server有個查詢優(yōu)化器的東西,大概是預(yù)加載了limit,在where環(huán)節(jié)找到數(shù)據(jù)后立馬停止。
本問題沒有找到官方說明,只是個人推斷。
推斷過程如下:
隨便找了一個省市區(qū)縣鎮(zhèn)的四級聯(lián)動的表,共46462條數(shù)據(jù),name字段為中文,無索引,所以該字段where是全表掃描。
把全表數(shù)據(jù)復(fù)制了32遍,共1486784條數(shù)據(jù),執(zhí)行select * from address where name like '%北京%',不加limit 1用時11.62秒,加上limit 1用時0.04秒,性能提升幾百倍,如果limit在where全部取篩選數(shù)據(jù)后在截取,指望著限制條目,性能就提升幾百倍,幾乎不可能。因為最耗時過程是where環(huán)節(jié)的全表掃描,所以才猜測是預(yù)加載了limit,在where查詢數(shù)據(jù)數(shù)量符合limit值時就直接中斷。