索引其實就是一種資料結構,目的就是為了幫助我們快速的檢索資料庫中的資料。就好比我們查字典的時候,會先翻一番目錄,這個目錄就是索引。
常見的mysql中主要有兩種索引:hash索引和b+樹索引。現在常用的儲存引擎是innodb,它預設就是b+樹。
雜湊索引底層是雜湊表,雜湊表是一以key-value結構儲存的,所以它是不連續的,也就不支援範圍查詢,也無法用於排序。同時hash是一種非常快的查詢方式,一般情況下的時間複雜度為o(1),也就是一般情況下只需要一次查詢就能定位資料。
mysql中memory引擎支援hash索引,因此查詢操作非常快。
當我們要利用索引的時候,顯然不能把整個索引全部都載入到記憶體。我們能做的就是逐一載入每乙個磁碟頁,這裡的磁碟頁就指的是索引的樹節點。所以樹的高度決定了io次數,也就決定了查詢效率。
b+樹的所有資料都儲存在葉子節點上,因此查詢單條資料的時候,查詢速度非常穩定。對於關係型資料庫來說,穩定的查詢可以更好的估算資料庫瓶頸。b+樹的葉子節點上有指標相連,因此在做資料遍歷的時候,只需要對葉子節點遍歷就可以了。同時這個特性也使得b+樹非常適合做範圍查詢。
innodb儲存引擎會監控表上各索引頁的查詢。如果觀察到建立索引可以帶來速度提公升,則建立雜湊索引,稱之為自適應雜湊索引(adaptive hash index,ahi)。
除了字面意思之外,普通索引和唯一索引在查詢和更新的時候有很大的區別。
為了減少隨機讀磁碟的的io消耗,innodb引入了change buffer。當更新乙個資料頁時,如果資料頁在記憶體中就直接更新,而如果這個資料頁還沒有在記憶體中的話,不影響資料一致性的前提下,innodb會將這些「更新操作」快取在change buffer中,這樣就不需要從磁碟中讀入這個資料頁。而當我們下次查詢需要訪問這個資料頁的時候,將資料頁讀入記憶體,然後執行change buffer中和這個頁有關的操作,生成乙個正確的版本然後返回結果。
如果乙個查詢操作命中普通索引,那麼查到第乙個滿足條件的記錄後,還不需要繼續往下查詢,直到第乙個部門組的記錄後停止。而唯一索引的話,由於索引定義了唯一性,查詢到第乙個不滿足的條件後就立即停止。
從查詢操作來看,唯一索引可能很美好查詢到第乙個記錄後就立即停止,但事實上帶來的效能差距卻是微乎其微的。因為innodb的資料本身是以頁為單位然後讀入記憶體,通常來說乙個資料頁可以放進千個key。除非我們查詢的記錄剛好是這個資料頁的最後一行,那麼讀取下一行記錄就必須載入下乙個磁碟頁。而這種情況出現的概率又很低,所以我們也可以認為「找到下乙個不滿足條件」這個操作成本對於現在的cpu來說可以忽略不計。
如果執行的是乙個更新操作,當資料頁已經載入到了change buffer中,那麼唯一索引和普通索引都可以直接把更新操作快取在記憶體中,只是唯一索引需要判斷有沒有插入衝突,當然這個判斷消耗cpu的時間也可以忽略不計;如果磁碟頁不在記憶體中,對於普通索引來說直接將更新記錄在change buffer執行語句就結束了。而對於唯一索引來說,需要將資料頁讀入記憶體,當判斷到沒有衝突之後才可以插入這個值。
經過上面你的分析,我們知道了change buffer可以加速更新操作,從而減少了隨機讀io的消耗。change buffer主要記憶體中的操作,那麼他的io瓶頸就發生在將記錄變動合併到到資料頁的時候,這個操作叫merge。也就是說如果merge的時候change buffer上的記錄的變動越多,那麼我們的快取命中率就越大。
對於寫多讀少的業務來說,乙個頁寫完然後立馬被訪問的概率會很少,這時候change buffer的效果最好。
而對於讀多寫多的業務來說,如果乙個操作寫入了change buffer,但是又馬上被訪問,此時就會立即出發merge操作。change buffer的目的是為了減少隨機io,而這種情況下不但不會減少,反而增加change buffer的維護代價。所以這種業務模式下可以關閉change buffer。
聚簇索引並不是一種單獨索引型別,而是一種儲存方式。innodb的聚簇索引實際上實在同乙個結構中儲存了b-tree的索引和資料行。所以如果表有聚簇索引,那麼資料是存放在索引的葉子頁中。innodb通過主鍵聚集資料,這樣使得乙個表只能有乙個聚簇索引,所以基於主鍵索引的查詢效能很高,因為葉子節點就是需要的資料。
而非聚簇索引的葉子節點就是索引,但是保留了乙個指向對應連線的資料塊。所以基於非聚簇索引的查詢,最後需要回到主鍵索引樹查詢剩餘資料,這個過程叫做「回表」。
如果乙個索引已經包含了我們需要的所有資料列,那我們就不需要再回表查詢額外的資訊,我們又稱這樣的索引為「覆蓋索引」。合理的建立覆蓋索引,也是常見的sql優化手段。
例如乙個使用者表我們建立了年齡和姓名的索引,當我們需要查詢18歲的使用者的姓名的時候如果使用:select age, name from user where age = 18;就可以通過覆蓋索引查詢,無需回表。
索引可以是由乙個列組成,也可以由多個列組成的聯合索引。
如果乙個表建立了聯合索引,那麼索引的任何字首都會用於查詢。因為b+樹是有順序的資料結構,當資料項是符合資料結構時候,b+樹就會按照從左到右的順序來建立搜尋樹。
比如上圖建立乙個a/b/c的聯合索引index_a_b_c,如果我們要查詢a=2 and b=1的資料。首先查詢a=2的行,當定位到第乙個a=2的行之後,繼續往下不斷的搜尋,如果發現比2大這時候就停止。因為是按照順序排序的當出現比2大的數的時候就說明已經全部找到了。這時候我們便獲得了乙個區間,這時候我們再在這個a=2的區間裡找到b=1的資料即可。
所以說我們建立a/b/c的聯合索引,在過濾a/b條件的時候就可以命中索引,為了驗證我們的觀點我們檢視一下執行計畫:
using index和key說明使用了index_a_b_c的覆蓋索引。
索引下推(index condition pushdown optimization)是mysql5.6的乙個重要優化,它是資料回表時候的乙個提前優化。
還是上圖那個表,這時候我們增加乙個欄位d索引結構不變,這時候我們執行一條sql語句:
select * where a=2 and c=1;
根據最左字首匹配原則是無法命中索引,因為c=1需要全表掃瞄。雖然我們最終還是要回表進行全表掃瞄,但是我們可以再回表的時候少掃瞄一些資料。索然無法利用索引過濾所有資料,但是我們利用索引找出a=2的資料,回表的時候就可以減少全表掃瞄的資料。這時候我們檢視一下執行計畫:
需要全表掃瞄,但是extra不為using where而是using index condition。這就是索引下推
首先當你建立了很多索引,具體選擇使用哪個是優化器的工作。
優化器的選擇索引的目的就是找到乙個最優的執行方案,並且用最小的代價去執行語句。在資料庫裡面,掃瞄行數是影響執行代價的原因之一。因為掃瞄的行數越少,就意味著訪問磁碟的次數越少,消耗的cpu資源也就越少。
例如:當我們對一張使用者表的性別建立了索引,這時候我們要選擇出所有男性使用者的資訊,通過索引找出所有的男性資訊的id,然後回表查出整行資料,這個代價優化器也要計算進去。但是如果我們直接在主鍵索引上掃瞄,那就不需要回表,也就沒有額外代價。
優化器會估算兩個選擇的代價,從結果上來看,優化器認為直接掃瞄主鍵索引更快。但也可能從執行時間上來看,這個選擇並不是最優的。這就是為什麼我們明明建立了索引,但是執行的時候並沒有通過索引。
從另乙個方面來看,我們建立索引的時候,一定要選擇區分度高的列來建立。區分度越高,也就意味著可以更快的鎖定資料庫中的行。
最後,如果感覺對你有幫助就來個二連吧:關注、點讚!
Android,你要掌握的一些東西
首先非常感謝以下的博主們,其次此篇部落格僅作分享。執行緒池 threadpool 全面解析 總結的挺好的,適合初學者 android效能優化之使用執行緒池處理非同步任務 這裡對android的幾種執行緒池做了比較詳細的介紹以及使用,包括自定義執行緒池 handler通訊機制的工作原理 詳細的介紹了,...
軟體測試基礎面試你必須掌握的
1 什麼是軟體測試?軟體測試的目的與原則?定義 在規定的條件下對程式進行操作,以發現程式錯誤 衡量軟體質量,並對其是否能滿足設計要求進行評估的過程。目的 在於發現錯誤 發現程式中存在的 或業務邏輯錯誤 檢驗產品是否符合使用者的需求 提高使用者體驗。原則 如二八原則 測試應盡早啟動 介入。2 什麼是軟...
面試官問這6個問題,你要警惕!
先做個自我介紹吧 談談你的優缺點 你打算怎麼開展工作 你的興趣愛好是什麼 這些問題在不少求職者看來都很傻很天真,難道我回答了面試官就真信了?根據前程無憂論壇 bbs.51job.com 近期就 最讓人反感的面試題 為主題進行的調查結果顯示,們最反感在面試時被問到自己的缺點,其次就是自我介紹,而離職原...