最近小農在找工做,由於今年疫情的特殊緣由,致使工做不是特別好找,因此一旦有面試電話,若是能夠,都會去試一試,恰好接到一個面試邀請,感受公司還不錯,因而就肯定了面試時間,準備了一下就去面試了。mysql
第一輪面試是小組組長面試,經過。
第二輪是經理面試也是經過了。
第三輪總監面試,前面都還有模有樣,忽然畫風一轉,面試官說:「問你最後一個問題」面試
面試官:10W條數據,我要從其中查出100條不連續的數據,給你id,來查name和password進行展現,如何才能高性能的去使用?sql
我:在id上創建聚簇索引,而後用 in id 來縮小表搜索範圍,最後 使用條件查詢 小於最大id,大於最小id,這樣可讓sql速度可以比較快的展現,雖然In的性能比較低
內心活動:雕蟲小技,還最後一個問題,這樣的問題再來一個吧數據庫
只見面試官緊鎖眉頭,與我內心期待的表情有點不同啊,難道是哪一個環節出了問題?
面試官:這樣的性能不能達到最優化的程度,並且若是我給你的最小id是1,最大id是100000呢?性能優化
你這就有點槓精了啊,那行吧,你是面試官你說了算
我:既然id已經給出來了,並且只查詢兩個字段,用聚簇索引那麼查詢數據是很快的,用in id應該是能夠的。數據庫設計
面試官:好的,回去等通知吧
我。。。。。性能
因而回去後,查詢資料,才知道原來面試官,真正想考的是 「覆蓋索引」優化
什麼是覆蓋索引:ui
當sql語句的所求查詢字段(select列)和查詢條件字段(where子句)全都包含在一個索引中 (聯合索引),能夠直接使用索引查詢而不須要回表。這就是覆蓋索引,經過使用覆蓋索引,能夠減小搜索樹的次數,這就是 覆蓋索引,在瞭解覆蓋索引以前,咱們先來看看什麼是索引。設計
咱們有一個主鍵列爲id的表,表中有字段name,而且在name上有索引
表中 t_user
值分別爲(1,張一)、(2,張二)、(3,張三)、(4,張四)、(5,張五)
表結構以下:
mysql> create table t_user (
id bigint(20) not null auto_increment ,
name varchar(255) not null,
primary key (id),
index index_name (name) using btree)
engine=innodb
default character set=utf8 collate=utf8_general_ci
兩棵樹的示例示意圖以下:
從圖中不難看出,根據葉子節點的內容,索引類型分爲主鍵索引和二級索引(非主鍵索引)。
主鍵索引: 主鍵索引的葉子節點保存着主鍵即對應行的所有數據。在InnoDB裏,主鍵索引也被稱爲聚簇索引(clustered index)。
二級索引(非主鍵索引): 二級索引樹中的葉子結點保存着索引值和主鍵值,當使用二級索引進行查詢時,須要進行回表操做。在InnoDB裏,非主鍵索引也被稱爲二級索引(secondary index)
經過上面所講的,咱們來看看如何經過sql語句來區分 主鍵索引和普通索引的查詢
select * from t_user where id=1
即主鍵查詢方式,則只須要搜索id
這棵B+樹select * from t_user where name=張三
即普通索引查詢方式,則須要先搜索name
索引樹,獲得id的值爲3,再到id
索引樹搜索一次。這個過程稱爲回表也就是說,基於二級索引(非主鍵索引)的查詢須要多掃描一棵索引樹。所以,咱們在應用中應該儘可能使用主鍵查詢。
看到這裏若是你看懂了上面的介紹,那麼這裏你會有一個疑問,我直接用in id
不就行了嗎,創建id主鍵索引,就能夠不用回表了,速度不也就提高了嗎?
若是是 5.5 以前的版本確實不會走索引的,在 5.5 以後的版本,MySQL 作了優化。MySQL 在 2010 年發佈 5.5 版本中,優化器對 in 操做符能夠自動完成優化,針對創建了索引的列可使用索引,沒有索引的列仍是會走全表掃描,也就是咱們所說的回表。
那麼,有沒有可能通過索引優化,避免回表過程呢?答應是有的
sql語句以下,其中id自增,name爲索引:
mysql> create table t_user (
id bigint(20) not null auto_increment ,
name varchar(255) not null,
password varchar(255) ,
primary key (id),
engine=innodb
default character set=utf8 collate=utf8_general_ci
好比有這麼兩句sql
語句A: select id from user_table where name= '張三'
語句B: select password from user_table where name= '張三'
語句A: 由於 name索引樹 的葉子結點上保存有 name和id的值 ,因此經過 name索引樹 查找到id後,所以能夠直接提供查詢結果,不須要回表,也就是說,在這個查詢裏面,索引name 已經 「覆蓋了」 咱們的查詢需求,咱們稱爲 覆蓋索引
語句B: name索引樹 上 找到 name='張三' 對應的主鍵id, 經過回表在主鍵索引樹上找到知足條件的數據
所以咱們能夠得知,當sql語句的所求查詢字段(select列)和查詢條件字段(where子句)全都包含在一個索引中(聯合索引),能夠直接使用索引查詢而不須要回表。這就是覆蓋索引。
例如上面的語句B是一個高頻查詢的語句,咱們能夠創建(name,password)的聯合索引,這樣,查詢的時候就不須要再去回表操做了,能夠提升查詢效率。
因此關於上面的面試題咱們就能夠得出,使用聯合索引就能夠很好的回答面試官的問題(id,name,password)這樣的聯合索引就能夠調用到覆蓋索引,能夠減小樹的搜索次數,再也不須要回表查整行記錄,顯著提高查詢性能,因此使用覆蓋索引是一個經常使用的性能優化手段。
說到了聯合索引咱們就不得不說聯合索引中最重要的匹配原則,最左匹配原則了
最左前綴匹配原則,是很是重要的原則,mysql會從左向右進行匹配。
例如咱們定義了(name,password)兩個聯合索引字段,咱們 使用 where name = '張三' and password = '2'
索引能夠生效的,當咱們是顛倒了他們的順序 使用where password = '1' and name = '王五'
,索引一樣也是能夠生效的,在mysql查詢優化器會判斷糾正這條sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃,咱們能儘可能的利用到索引時的查詢順序效率最高,因此mysql查詢優化器會最終以這種順序(where name = '張三' and password = '2'
)進行查詢執行,就相似 咱們的 order by name,password
這樣一種排序規則,先對張三的用戶進行查詢排序,在對password進行處理
好比咱們要查詢姓張的用戶,咱們的條件查詢能夠爲 "where name like ‘張%’"
,可是不能是 where name like '%張%'
或者是 where name like '%張'
,由於索引能夠用於查詢條件字段爲索引字段,根據字段值必須是最左若干個字符進行的模糊查詢,也就是須要是 '張%' 這樣的添加纔可使用。
索引的複用能力。由於能夠支持最左前綴,因此當已經有了(name,password)這個聯合索引後,通常就不須要單獨在name上創建索引了。所以,第一原則是,若是經過調整順序,能夠少維護一個索引,那麼這個順序每每就是須要優先考慮採用的。
若是既有聯合查詢,又有基於name,password各自的查詢呢?查詢條件裏面只有password的語句,是沒法使用(name,password)這個聯合索引的,這時候你須要同時維護(name,password)、(password) 這兩個索引。
建立索引時,咱們也要考慮空間代價,使用較少的空間來建立索引
假設咱們如今不須要經過name查詢password了,須要經過name查詢age或經過age查詢name
name字段是比age字段大的,因此,選擇第一種,索引佔用空間較小的一個
上面咱們說到知足最左前綴原則的時候,最左前綴能夠用於在索引中定位記錄。那麼若是那些不符合最左前綴的部分,會怎麼樣呢?
若是如今有一個需求:檢索出表中「名字第一個字是張,並且沒有刪除的信息(is_del = 1)。SQL語句以下:
mysql> select * from t_user where name like '張%' and is_del=1
在MySQL 5.6以前,只能從匹配的位置一個個回表。到主鍵索引上找出數據行,再對比字段值
在MySQL 5.6中 引入的索引下推優化(index condition pushdown), 能夠在索引遍歷過程當中,對索引中包含的字段先作判斷,直接過濾掉不知足條件的記錄,減小回表次數
根據(username,is_del)聯合索引查詢全部知足名稱以「張」開頭的索引,而後回表查詢出相應的全行數據,而後再篩選出未刪除的用戶數據。過程以下圖:
每個虛線箭頭表示回表一次
圖一(無索引下推執行流程)
每個虛線箭頭表示回表一次
圖二(索引下推執行流程)
圖1跟圖2的區別是,InnoDB在(name,is_del)索引內部就判斷了數據是否邏輯刪除,對於邏輯刪除的記錄,直接判斷並跳過。在咱們的這個例子中,只須要對ID一、ID4這兩條記錄回表取數據判斷,就只須要回表2次
mysql默認啓用索引下推,咱們也能夠經過修改系統變量optimizer_switch的index_condition_pushdown標誌來控制SET optimizer_switch = 'index_condition_pushdown=off';
咱們也須要注意:
今天的內容就到這裏了,咱們在上面描述了數據庫索引的概念,包括了覆蓋索引、聯合索引、索引下推,那麼下次若是有面試官問你剛開始的問題,相信你們能夠好好的回(dui)答(ta)一下面試官了,在sql優化中,減小回表次數,或者直接使用覆蓋索引是比較重要的,儘可能少地訪問資源也是數據庫設計的重要原則之一,謝謝你們,加油~