MySQL索引底層原理相關問題自總結(難度對標18K-25K薪資,已總結80+,持續更新中)

小松聊PHP进阶發表於2024-03-09

注:以下所有內容均為自己總結的筆記,涉及底層原理,難度對標18K-25K薪資,偏理論,不保證百分百準確性。

索引查詢快速的原理?

建立索引的本質是排序,排好序之後再找資料就快了。
對於B+tree索引,B+tree對資料排序後採用多路查詢思想的非線性查詢方案,減少了大量的查詢次數,從而避免多次磁碟io,進而快速找到結果。

為什麼推薦用自增id做主鍵?

自增id直觀,且不用刻意維護這個欄位,減少工作量,還能避免主鍵更新引起的頁分裂。
舉例說明頁分裂:資料是存在頁上的,頁1儲存id為1、2、5的資料,如果沒有設定自增,如果突然新增了id為3、4的資料,頁1無剩餘空間儲存,就需要將頁1資料進行拆分,頁1儲存id為1、2、3的資料,頁2儲存id為4、5的資料,分裂的目的是為了排序,排序的目的是為了方便查詢。分裂需要消耗計算資源用於更改資料,這種非必要發生的操作就儘量避免。

什麼是連結串列 ,在索引中起到了什麼作用?

連結串列是一種線性資料結構,由節點組成,每個節點包含兩部分:資料和指向相鄰節點的指標。
連結串列分單向和雙向。
單向:節點只有一個指標,指向下一個節點
雙向:每個節點有兩個指標,一個指向前一個節點,一個指向後一個節點。
根據對連結串列的操作,又可以分為佇列和棧。
佇列:先進先出(LPush->RPop,或Ppush->Rpop)。
棧:先進後出(LPush->LPop,或RPush->RPop)。

MySQL InnoDB引擎和MyISAM引擎,都用的B+tree演算法作索引,在葉子節點,每個節點間使用向左或者向右的指標,來移動指標,這也是索引支援區間查詢的原因,葉子節點間組成一個連結串列。

什麼是B+tree?

是一種透過排序,方便查詢的資料結構,特別是在資料庫和檔案系統的實現中廣泛應用。它是一種平衡樹的變體。
平衡性: 所有葉子節點都位於同一層,使得在樹的高度方面達到平衡,從而保持高效的查詢、插入和刪除操作。
有序性: B+樹的葉子節點按照鍵值大小順序儲存,使得範圍查詢變得更為高效。
多路搜尋: 每個非葉子節點都有多個子節點,允許更多的分支,提高搜尋效率。
適用於範圍查詢: 由於有序性和多路搜尋的特性,B+樹在範圍查詢方面表現優秀。
在資料庫系統中,B+樹常被用作索引結構,用於加速對資料庫表的查詢操作。其設計考慮了磁碟儲存的特性,使得在磁碟上的讀寫操作更為高效。

B+tree比二叉樹好在那裡?

更加平衡:二叉樹在遇到一組非混亂的資料集合下,樹的層級會變的很高,意味著io的次數變多,B+tree避開了這個問題。
多路搜尋:二叉樹為雙路搜尋,B+tree為多路搜尋,極大提高了搜尋效率。
查詢方便:無論是區間查詢還是定值查詢,B+tree都比二叉樹查詢方便,二叉樹需要中序遍歷才能得到有序序列。

Btree與B+tree區別?

Btree:非葉子節點存放資料,葉子節點無指標,支援區間查詢。
B+tree:非葉子結點不存放資料,葉子節點有指標,支援更快速的區間查詢。
正因為Btree非葉子節點存放資料,查詢起來無法像B+tree一樣葉子節點間依靠連結串列進行範圍查詢,所以區間查詢效率低。
Btree做查詢,需要在葉子節點和非葉子節點之間來回跳躍搜尋,來回的跳躍,意味著需要更多的磁碟io,而B+tree只需要從非葉子節點到葉子節點即可,
所以穩定性不如B+tree。

為什麼MySQL採用B+tree?

查詢高效:因為B+tree採用多路非線性查詢思想,降低樹的層級,減少磁碟io。
支援區間查詢:因為資料全在葉子節點上,每個節點之間有指標做關聯。
演算法穩定:不會向二叉搜尋樹那樣,層級和資料有關,查詢情況時好時壞。

頁根頁之間怎麼關聯?

雙向連結串列。
雙向連結串列因為有序,所以可以適用二分查詢。
雙向連結串列,鏈的是行中儲存的後設資料,這歸行格式管理,行格式儲存的其中一項叫做record_type,有4個值,0表示普通使用者記錄,1表示目錄項紀錄,2表示當前頁的最小值,3表示當前頁的最大值。
使用0和1區分是目錄頁還是資料頁。使用2和3走索引時用於定位,進行區間或等值查詢。

為什麼資料在頁上,頁本身要加索引?

雖然一個頁可以儲存多條資料,但是在大資料情況下,一個頁不夠存就需要多個頁,為了避免查詢資料時資料不用對大量的頁挨個遍歷。與是也加上了B+tree用於查詢。

B+tree 葉子節點之間怎麼關聯

雙向的指標。

什麼是聚簇索引?

在InnoDB引擎中,索引的葉子節點儲存的是實際的資料行,資料即索引,索引即資料。
好處就是能帶來快速的查詢速度,透過索引就可以找到實實在在的資料。
一般一個表只能有一個聚簇索引,一般為主鍵,因為資料的排列就是按照索引來的,如果一個表中有多個聚簇索引,一是不知道二級索引參考哪個,二是太佔空間。

MyISAM有聚簇索引嗎?

沒有。
檢視MyISAM的二進位制檔案,有.MYI(儲存索引)和.MYD(儲存資料)字尾結尾的檔案,他們的索引和資料是分開的,不符合資料即索引,索引即資料的特點。
MyISAM的葉子結點儲存的是資料的位置資訊。

MyISAM中B+tree的葉子節點儲存的是資料的地址,也需要類似回表的操作,為什麼效能也不慢?

因為定址的效能也挺高的,如果速度慢,就不會這麼主流了。

為什麼InnoDB比MyISAM有更好的併發效能,是因為索引上有什麼不同之處嗎?

不是,併發性和不同引擎的索引沒有太多相關性。
InnoDB有更好的併發效能,是因為它支援粒度更小的行級鎖,併發情況下,事務用於保持資料一致性,鎖是併發控制必備的機制。

為什麼InnoDB不推薦用較長的資料做主鍵?

大資料情況下,InnoDB引擎建立的二級B+tree索引,葉子節點是主鍵,較長的主鍵,會佔用更多的位置。
而MyISAM中B+tree的葉子節點,儲存的是資料的位置。

如何區分一棵B+tree是不是聚簇索引?

看這顆樹的葉子節點上,儲存的是實實在在的資料,還是根據當前列關聯的主鍵。

聚簇索引的優點?

把實實在在的資料當索引,不用回表,效能很高,因為透過索引找到的那條資料,就是所在行的資料。
InnoDB引擎,MySQL 預設情況下使用自增主鍵作為聚簇索引,這便是主鍵查詢快的原因。

聚簇索引的缺點?

如果插入的資料不是自增的數字id,可能引起索引分裂,降低效能。
佔用較大的空間。

為什麼主鍵查詢效能很高?

InnoDB引擎,MySQL 預設情況下使用自增主鍵作為聚簇索引,不用回表。這便是主鍵查詢快的原因。
具有唯一索引,定值查詢,查到後不必接著找。

為什麼不建議用UUID作為主鍵?

避免索引分裂,影響插入資料時的效能問題。
必須要明白,索引的本質是排序,索引查詢的本質是根據排好序的資料進行查詢。
可能後生成的uuid,根據ASCII碼字典排序,會排到先生成uuid的前面,插入新值,則需要重新排序,就要破壞掉原本的索引結構,這個過程將消耗時間和算力。

c3dc38e1-8db2-4e9f-9fe4-735e88facdb4,像是這種型別的資料叫做uuid。
一般有兩個好處:

  1. 在分散式環境下保證唯一性,因為夠長且重複機率太低,否則,A模組的id=1,可能會與B模組的id=1混淆。
  2. 駭客不容易猜到相鄰的uuid是什麼,就算程式有越權漏洞,也不會很難根據原ID猜測其它ID。

其次是uuid,佔用空間比int型別更大,使得其它二級索引,儲存主鍵時,佔用更多的空間。

用自增id就不會出現索引分裂的情況嗎?

不是的。
自增的有序id,只會減少插入資料時的分裂,當大資料時的新增引起的B+tree分層,或者對資料的的插入和刪除操作,都可能為了區域性重建索引,觸發分裂操作。

什麼是非聚簇索引?

非聚簇索引則將索引與實際資料行分開儲存,索引的葉子節點儲存的是當前索引值與主鍵(MyISAM則是當前索引值與行地址),不是所在行的資料。

什麼是二級索引?

級索引通常指的是除了表的主鍵之外建立的額外索引。

什麼是輔助索引?

一般是指非聚簇索引或者二級索引。

什麼是回表?

InnoDB引擎,在非聚簇的B+tree索引上,樹的葉子節點儲存的是當前索引欄位資料和主鍵的值,當前的葉子節點資料與主鍵做邏輯上的關聯,而不是儲存所在行的全部資料,所以需要根據主鍵,再次查詢一遍資料,這個過程叫回表。
因內部多了一輪的查詢流程,所以效能有所降低,所以能用主鍵查詢的場景,就不要使用其它欄位。

為什麼不透過像聚簇索引一樣的方式避免回表?

技術上能實現,但是缺點很明顯,空間換時間的代價太大。
在非聚簇的B+tree上,樹的葉子節點重複儲存所在行的值,會造成大量的空間浪費。
其次是更新代價太大,可能更新一小塊資料,就需要對這些索引上的資料做同步更新。
所以做邏輯關聯更好。

為什麼插入、更新、刪除資料時,非聚簇索引比聚簇索引效能略高?

插入資料引起的索引分裂問題,非聚簇索引只需要調整當前索引的位置和主鍵就好,而非聚簇所以需要移動整行的資料。

什麼是聯合索引?

組合索引、複合索引、多列索引,聯合索引一個意思,多個欄位組合去建立索引。
索引排序規則:先按照左邊的欄位進行排序,如果左邊欄位相同,再根據右邊的欄位排序。

什麼是字首索引?

MySQL 字首索引是一種,它只對列值的前部進行索引,而不是對整個列值進行索引。
做法:alter table 表名 add index (filed_name(長度));
優點:主要用於控制索引大小,由於底層在比較時字串長度較短,所以比較起來也比單列索引塊。
缺點:有誤差,所以需要在空間和產品服務方面做取捨。
評估設定長度:需要根據資料情況測試,如果count(distince left('欄位名', 10)) / count(*) 等於1左右,說明擷取欄位前10個字元去重後的數量,等於總數,說明依照前10個字元就能辨識度很高。如果結果略小於1,說明辨識度還不夠,可以取12,如果遠小於1,長度取20再試試,以此類推。
補充:《阿里巴巴Java開發手冊》【強制】在varchar簡歷索引時,必須指定索引長度,沒必要對全欄位建立索引,根據實際文字區分度決定索引長度。

聯合索引算不算聚簇索引?

相似但不算,聯合索引關聯的不是所在行的全部欄位,而是部分欄位。

為什麼單表資料不能超過2000萬條

這是個粗略的理論值,很多人說超過這個數,會把B+tree的層級轉為4層,其實不準的。
這個還是要看葉子節點資料的大小,如果葉子結點很大,需要更多的頁,則存不了太多,如果葉子節點資料很少,有人推算,存1個億也沒問題的。

先排除一些後設資料的儲存:資料儲存在頁上,每頁大小16KB,每頁需要開闢一些新的空間來儲存後設資料(例如指向上一頁下一頁的指標),頁頭儲存檔案頭38位元組,頁面頭56位元組,最小記錄和最大記錄26個位元組,為了保證不出錯,出現了校驗和的機制,這塊功能的儲存被放到了頁尾,佔8個位元組。頁裡的資料呢,為了方便查詢每行的資料,所以包含頁目錄(採用二分法,把查詢複雜度從O(n)最佳化為O(log n)),這也佔空間,這些可以粗略的估計為佔用了1KB。

宣告代數:假設非葉子節點指向葉子節點的指標數量為X,葉子節點能夠容納的行數為Y,B+tree層數為Z,那麼能儲存的總行數就是Xz-1 * Y。

計算X:主鍵假設用bigint,佔8個位元組,頁號這個後設資料佔4個位元組,非葉子節點一條資料佔12個位元組,15KB / 12B = 1280。
計算Y:假設一個行資料為1KB,也就是說可以放15條資料。

若Z為1:12800 * 15 = 15行
若Z為2:12801 * 15 = 19200行
若Z為3:12802 * 15 = 24576000行
若Z為3:12803 * 15 = 31457280000行

但是這是理想情況,很多主鍵id都用無符號int,能節省4個位元組,行數大小也不確定,所以這是個理論值,究竟是多少,需要根據實際情況討論。

什麼是最左匹配原則?

生效的情況:
abc建立聯合索引,where a = 'v1' and b = 'v2' and c= 'v3',where順序可以顛倒,但是必須都是and,左邊的列不能包含區間查詢。
失效的情況:
多欄位建立聯合索引,如果聯合索引左邊的欄位的查詢條件不存在,或者聯合索引左邊欄位使用的區間查詢,或者使用了or,都會導致索引失效。
注意,這裡說的順序,是聯合索引的順序,不是where條件的順序。

底層什麼原因導致最左匹配原則?

B+tree聯合索引排序,是根據ASCII碼的字典順序進行從左到右依字元排序,然後依欄位從左到右排序,沒有其它方向的排序,這就不能相容更多種的查詢方式。

假如abc三個欄位建立聯合索引,where a = v1 and b = v2 and c = v3,此時欄位索引的使用情況?

每個欄位都能用上索引。

假如abc三個欄位建立聯合索引,where a = v1 or b = v2 or c = v3,此時欄位索引的使用情況?

a能用上索引,b和c都無法使用索引,因為and是屬於流水線式的篩選,而or是與前面的搜尋條件不相關的個體,b和c都沒有左邊的欄位配合成為聯合索引。

假如abc三個欄位建立聯合索引,where a = v1 and b > v2 and c = v3,此時欄位索引的使用情況?

a能用上索引,b能用上索引,c無法使用上索引,因為b是區間查詢導致c無法按索引查詢。

假如abc三個欄位建立聯合索引,where a = v1 and c = v3,此時欄位索引的使用情況?

a可以用上索引,c用不上索引,因為缺少b。

假如abc三個欄位建立聯合索引,where b = v1 and c = v3,此時欄位索引的使用情況?

b和c都用不上索引,因為缺少a。

假如abc三個欄位建立聯合索引,where a = v1 and b = v2 and c = v3 or c is null,此時欄位索引的使用情況?

abc三個欄位全部能用上索引。

假如abc三個欄位建立聯合索引,where a = v1 and b = v2 and c = v3 or d + 1 = 10,此時欄位索引的使用情況?

用explain實測,type為all,索引用不上了,全表查,因為使用了表示式。

假如abc三個欄位建立聯合索引,where a = v1 and b = v2 and c = v3 and d + 1 = 10,此時欄位索引的使用情況?

用explain實測,type為ref,並根據key_len欄位評估,abc都能使用索引。

假如abc三個欄位建立聯合索引,where a = v1 and b = v2 and c = v3 and length(d) < 20,此時欄位索引的使用情況?

用explain實測,type為ref,並根據key_len欄位評估,abc都能使用索引。

假如abc三個欄位建立聯合索引,where a = v1 and b = v2 and c = v3 or length(d) < 20,此時欄位索引的使用情況?

用explain實測,type為all,索引用不上了,全表查,因為使用了函式。

假如abc三個欄位建立聯合索引,where a = v1 and b = v2 and c = v3 or c = v4,此時欄位索引的使用情況?

用explain實測,type為all,索引用不上了,全表查,因為c列沒有建索引。

假如abc三個欄位建立聯合索引,where a = v1 and b = v2 and c = v3 and c = v4,此時欄位索引的使用情況?

用explain實測,type為ref,並根據key_len欄位評估,abc都能使用索引。

假如abc三個欄位建立聯合索引,where a > v1 and b = v2 and c = v3,索引失效,explain type為all,資料量大還經常查詢,怎麼辦?

建立bca的聯合索引即可,alter table 表名 add index(b,c,a),每個欄位,最好加上索引長度。
abc的聯合索引,如果用不上,刪掉。alter table 表名 drop index 索引名。

為什麼不推薦mysql頻繁使用null值?

null值是個特殊的存在,在sql查詢上,即使是唯一索引列,也允許插入多個null值,這影響了了唯一索引的唯一性約束。
其次有些查詢,用where field = '',或者where filed = null,都是匹配補上的,只能用is null。
null值影響聚合函式的使用,導致count(欄位)結果不符合真實情況。
否則就難以區分到底是沒有關聯記錄還是其他情況。

什麼是索引下推?

索引下推簡稱ICP,最佳化SQL執行的一種策略,將where條件下推至儲存引擎執行,減少回表的資料量提高效能。
一般是針對二級索引說的,有多個where條件時,執行完第一個條件不著急回表,用剩餘的資料再次執行第二個where條件,減少回表的資料量。
索引下推常見在聯合索引中,只有使用了聯合索引中的欄位的時候,才可以。
舉例:百萬條資料,第一次where之後剩下1000條,執行完第二次where後身下5條,只需要回表5條資料即可,避免第一次where條件剩下的1000條資料回表,然後在執行第二個where,再回表。

為什麼有覆蓋索引時,不支援索引下推?

使用了覆蓋索引,說明索引的資料滿足了當前select查詢,不需要回表,已經不需要下推了。
索引下推有個暗含的前提,是索引無法完全滿足當前查詢前提的最佳化策略,且where的欄位又包含在聯合索引中,索引下推的終極目的是減少回表資料數量,既然不需要回表,那就不需要下推。

為什麼相關子查詢,不支援索引下推?

相關子查詢,指的是子sql與父sql的引數動態關聯,這會導致子SQL語句的引數處於動態(不確定)狀態,導致索引下推的目標都無法確定,所以不行。

什麼是mysql filesort?

這是mysql explain中Extra中可能會展示的東西,當然也是一種機制,在order by的場景中去用。
這種方式指的是在記憶體中排序,效率略低,因為沒有按照索引排序,儘量避免。
與之相對的,有個index排序,可以按照索引自然而然的排序,效率偏高。

filesort兩種排序演算法是什麼?

雙路排序:相對較慢。先找到orderby的列,然後排序,再根據排序的欄位查詢其它欄位,類似回表,所以慢。其次相對於單路排序更可能發生隨機io,order by排序後的資料,可能不在同一個頁上,這個過程需要來回的讀取頁中的資料。
單路排序:相對較快,根據orderby的列,一次性取出來所有欄位,然後再排序。

什麼是覆蓋索引?

要查詢的欄位上有索引,索引中的欄位涵蓋了select欄位的結果,因為不需要回表操作去查詢整行資料,避免回表的隨機io(回表的資料可能不在同一個頁上),這是一種效能最佳化的提現。
需要儘可能少select 欄位的數量,避免使用select *。

什麼情況下not in、is not null、<>、!=,左%能用上索引?

覆蓋索引。
要查詢的欄位上有索引,不用回表,卸掉了一個重擔,MySQL最佳化器認為這代價不大,所以選擇用索引。
這種情況下,explain type為index。

什麼是Hash索引?

基於雜湊表實現的索引結構,用於快速定位資料的儲存位置。在Hash索引中,索引鍵透過雜湊演算法計算得到一個雜湊值,該雜湊值指向儲存資料的具體位置,從而實現快速的查詢和定位。

MyISAM和InnoDB都支援雜湊索引嗎?

都不支援,只有Memory引擎支援。
並且不支援區間查詢,所以索引主要依靠B+tree。

where條件的順序會影響使用索引嗎?

如果都是and,則不影響。
MySQL有個東西叫做最佳化器,它會根據查詢的欄位,篩選的欄位,索引情況自動調整。
order by調整順序會影響結果。

where條件左邊的or遇見右邊的and,誰會先執行?

or的優先順序比and更小,會先執行右邊的and,再執行左邊的or,所以要控制好。避免索引失效。

那些查詢適合建立索引?

  • 需要唯一性約束兜底的欄位。
  • 經常被查詢或者作為where條件的欄位,=、>、<、<=、>=、in、between、like 右百分號
  • 經常group by或者order by的欄位。
  • delete或update被作為where條件的欄位。
  • distinct的欄位。
  • join on的連線欄位需要加索引,但是需要型別一致,因為MySQL內部有用函式做隱式轉換,用了函式就不適用索引。
  • 區分度(不重複度)高的欄位。
  • 把搜尋最頻繁的列,放在聯合索引的左側,(受聯合索引的最左原則影響)。

那些查詢不適合建立索引?

  • 資料量小,一個表,例如配置表,總類別表,可能最多幾十條記錄,建立不建立區別不大。
  • 寫多讀少,資料的寫操作對索引欄位的開銷比沒有索引要大,而且讀操作還少。
  • 區分度低的欄位,例如性別狀態等,這會導致線性查詢,能提升搜尋效率,但是不明顯,可加可不加。
  • sql語句包含<>、!=、not in、is not null,無法使用索引,所以專門用作排除性查詢的,不建議建立索引。

哪些情況下索引會失效?

  • 使用not in、is not null、<>、!=、這種排除法時會導致索引失效,覆蓋索引除外。
  • 最左匹配原則,左邊的欄位缺少時會出現,覆蓋索引除外。
  • 最左匹配原則,左邊的欄位有區間查詢,導致右邊的欄位無法使用索引。
  • like左邊或兩邊加百分號。
  • 型別的隱式轉換,如varchar的欄位,使用where varchar_field = 123,包括join表,用on連線的欄位。
  • where條件有函式,或表示式。
  • where語句包含or,or中存在非索引列。
  • 大資料量對二級索引欄位排序,如果select * 或者其它欄位,這個過程涉及回表,可能無法使用索引,因為資料量大,走索引的每條資料都需要回表,代價會很大。
  • order by欄位,如果排序與索引順序不一致,則可能導致索引失效,如果order by的每個欄位,都按照索引的順序,或者反順序,則仍舊會走索引。

如果一個表中的每個欄位都加了單列索引,且每個欄位都查詢條件的操作,哪個索引會被優先使用?

這是個開放性的問題,不能一概而論,不過可根據SQL語句的執行順序判斷,先執行sql語句的某個部分,如果這個部分有索引,那就使用這個索引,其它索引在此查詢過程中用不上。
可使用explain參考。

B+tree和Hash索引的適用場景?

B+tree:等值查詢,區間查詢,批次等值查詢,order by。
hash:等值查詢。

為什麼大資料情況下,使用select *,並對某個或某些二級索引列order by,會導致索引失效?

因為資料量大又使用select * ,會導致二級索引的排序需要回表,這個太耗資源。所以MySQL最佳化器選擇了全表查。
親測加一個limit資料量不超過49000,就能解決這個問題,explain type由all變成了index。

為什麼唯一索引比普通索引略快 ?

唯一索引找到資料就不往下找了。

一次查詢,表中多個索引都可用,MySQL最佳化器只能選一個嗎?

多數情況下是,但是還有index_merge(explain type的值)的情況。使用了多個單列索引來執行查詢。當在查詢條件中存在多個列,且每個列都有單獨的索引時。

什麼是自適應雜湊?

InnoDB不支援hash索引,但是提供了一個自適應的雜湊索引,屬於MySQL內部的最佳化機制。
某些資料被經常訪問,滿足等值查詢的時候,就會將這個資料頁的地址存放到雜湊表中,下次查詢的時候直接用。
MySQL5.7和8預設都是開啟的狀態,可使用select @@innodb_adaptive_hash_index來檢視。

為什麼很多人都在講索引就不得不提磁碟io?

因為最耗時的環節就在磁碟io上,索引就是為了減少磁碟io的次數。

欄位被刪除時,索引的情況?

單列索引,欄位被刪除時,同步刪除索引。
聯合索引,部分欄位被刪除時,索引刪除,自動根據剩餘欄位重建索引。索引全部欄位被刪除時,索引刪除。

什麼是降序索引?

這是mysql8的新特性,建立索引時指定索引的排序方式為降序,CREATE INDEX idx_column_name ON table_name (column_name DESC);
對建立聯合索引的列,進行不同的排序順序時,使用降序排序,會提高效能。
前提是,需要保證建立索引的升降序與排序的升降序一致。

為什麼子查詢比join慢?

子查詢的過程建立了臨時表,臨時表的建立和銷燬會佔用時間。
而join的過程,會產生一個結果集,這個結果集不是臨時表。

count(*)、count(1)、count(欄位),count(id)怎麼選,哪個效能高?

如果想統計全部資料,不推薦用count(欄位),因為遇到null值不會+1,如果統計某欄位數量,用count(欄位)。
效能方面,同一個引擎下差不多。
不同引擎下,MyISAM比InnoDB引擎效能高,MyISAM中,表的後設資料就存了count值,透過表級鎖自動維護一致性。時間複雜度為O(1)。InnoDB採用行級鎖和MVCC機制(事務相關分為當前讀和快照讀之分,不同的事務隔離級別和讀出的資料不一致,會造成不準確的情況),無法採用MyISAM的方案,所以要全表掃描,時間複雜度O(n)。

sql執行順序where優先於limit,在不加索引的列使用where,為什麼大資料情況下加limit 1能顯著提升效能?

sql的執行順序是:form->join on->where->group by->having->select->order by->limit,確實where在limit前面。
但是MySQL Server有個查詢最佳化器的東西,大概是預載入了limit,在where環節找到資料後立馬停止。
本問題沒有找到官方說明,只是個人推斷。
推斷過程如下:
隨便找了一個省市區縣鎮的四級聯動的表,共46462條資料,name欄位為中文,無索引,所以該欄位where是全表掃描。
把全表資料複製了32遍,共1486784條資料,執行select * from address where name like '%北京%',不加limit 1用時11.62秒,加上limit 1用時0.04秒,效能提升幾百倍,如果limit在where全部取篩選資料後在擷取,指望著限制條目,效能就提升幾百倍,幾乎不可能。因為最耗時過程是where環節的全表掃描,所以才猜測是預載入了limit,在where查詢資料數量符合limit值時就直接中斷。

相關文章