序言
在上一篇文章中主要講了索引的底層實現。主要討論了爲什麼加了索引以後,數據庫的查詢效率會加快?底層怎麼實現的?這個問題。
那麼本文主要想討論下以下幾個問題
- 怎麼建立合適索引,建立索引有哪些需要規避的要點?
- 爲什麼有時候建立了索引也沒啥子用?
以下是自己最近學習和使用的一些想法,希望大家可以互相交流,反正是技術交流哈哈。
首先拋出兩個語句乾貨。大家可以先去自己的數據庫執行檢查下。
– 查詢冗餘索引,看看是否有累贅索引
SELECT * from sys.schema_redundant_indexes;
– 查詢從未被使用到的索引,用都沒用過的,留着做啥。
SELECT * from sys.schema_unused_indexes;
怎麼建立合適索引,建立索引有哪些需要規避的要點
首先我們要知道,索引並不是越多越好。雖然索引用的很爽。
因爲底層是B+樹結構,我們在增,刪,改操作中都會對B+樹結構進行調整。
所以索引過多也會影響此類操作。主要考慮在where和order by 後面涉及的列建立索引。
同時,由於一條mysql在執行的時候,如果有多個索引命中,最終只能選擇其中一個索引。
所以個人見解:
要點一:複合索引和單列索引相比,應優先使用複合索引。
針對建立複合索引之前,需要先做一個調研把哪個字段作爲最左前綴。
調研可以從這幾個方面去判斷
1.有哪個字段是經常會使用到的,且大部分sql中都會有值。
舉個例子:
user表裏有id,name,age,address,sex等字段。
調研發現多數sql中都使用到了name這個字段作爲查詢條件。因此可建立複合索引idx_name_age_address(name,age,address)
此處使用到的是mysql裏最常見的最左原則。爲什麼會有這個原則,和底層原理有關,在上一篇文章中也敘述了。
2.字段長度小的列放在左側。因爲字段長度小,每一頁能存儲的數據量就越大,IO性能越好,也就是越快找到目標數據。
3.如果創建的複合索引中某個字段中的值都是不同的,那麼他的數據區分度就高,走索引效率就非常明顯,考慮放在左側。
這種複合索引比建立單列索引實用的多。
要點二:值比較稀少的列不建議使用索引。
比如上面例子中的sex字段。換句話說,建了索引也對查詢效率起不了作用。那麼爲啥會有這種論斷。
因爲系統在執行一條sql的時候,會進行預測走這個索引和全表掃描哪個掃描的行數少。掃描行數越少,I/O操作次數越少。
而我們走索引的時候,會通過sex這個索引先查到主鍵索引,再通過主鍵索引來查找數據。也就是會走兩次索引。也就是回表。
系統通過索引的區分度來判斷,索引上不同的值越多,這個基數越大,那麼走索引查詢越有優勢。
如果我們業務需要強制走某個索引查詢的話。可以使用select * from user force idx(age) where address = '中國'
要點三:儘量建立覆蓋索引
所謂覆蓋索引就是此索引覆蓋所有需要查詢的字段的值。尤其是查詢頻繁的語句,優先考慮覆蓋索引。上述例子中
建立了複合索引idx_name_age_address(name,age,address)
查詢語句:select id,name from user where name = '張三' and age = '20' and address = '中國'
因爲B+樹的葉子節點存儲的是主鍵+列值,最終還是要回表,就會比較慢。但覆蓋索引要查詢出的列和索引是對應的,不需要做回表操作。
我們往往會因爲偷懶或者想讓sql能夠複用,而使用select * from 的寫法。但這種無法使用覆蓋索引,也會消耗更多的CPU和IO
判斷建立的索引是否有效
要點一:判斷是否符合了最左前綴原則。
以上述例子中爲例:
建立了複合索引idx_name_age_address(name,age,address)
查詢語句:select * from user where age = '20' and address = '中國'
此語句因爲最左前綴爲name,但是name沒有作爲條件查詢,無法使用索引。
要點二:查看where 子句左邊是否有進行函數,算術運算或者其他表達式運算
select * from user where age+1 = 20 -- 不能使用索引
select * from user where age = 20 -1 -- 能使用索引
要點三:儘量避免在WHERE子句中使用!=或<>操作符,將打算加索引的列設置爲 NOT NULL。否則將導致引擎放棄使用索引而進行全表掃描。
要點四:查詢語句有多個索引,數據庫選錯索引。
數據庫使用採樣的方式來預測各個索引的基數。既然是採樣,就有可能失誤。
若系統判斷當前索引基數過小,就不走索引,直接全表掃描。
所以不要對每個單列建立索引。索引需要的是有效,而不是多。
要點五:聯合索引的第一個索引使用了範圍查找導致失效
以上述例子中爲例:
建立了複合索引idx_name_age_address(name,age,address)
查詢語句:select * from user where name in ('張三',‘小敏’) and age = '20' and address = '中國'
在此sql中,name字段會用到索引,但是後面的age和address索引失效。
因爲一個 SQL 只能利用複合索引中的一列進行範圍查詢。
所以如果有範圍查詢的字段可以放在複合索引的右邊。不要使用not in
not in 通常會讓索引失效,可以用left join 或者 not exist 替代。
要點六:排查是否使用了子查詢
儘量不使用子查詢,可用join替代。
因爲子查詢的結果集會存儲在臨時表中,而臨時表是沒有索引的。同時也會消耗過多的CPU和IO。也是慢sql的一部分原因。
總結
其實mysql使用B+樹作爲自己的數據結構,它是非常強大的。千萬數據量的大表,如果索引能夠使用的合適,也完全能夠支撐。
索引的重要性不言而喻,甚至在做開發設計評審時,索引也應該作爲數據庫設計裏重要的一項進行評審。
如果文中有說的不正確的地方,希望大家能夠互相交流,一起進步。