並不是索引越多越好,索引是一種以空間換取時間的方式,所以建立索引是要消耗一定的空間,況且在索引的維護上也會消耗資源。本文首發我的個人博客mysql索引不生效
這裏有張用戶瀏覽商品表,建表語句:
CREATE TABLE `product_view` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`server_id` int(11) NOT NULL,
`duration` int(11) NOT NULL,
`times` varchar(11) NOT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `time` (`time`),
KEY `user_product` (`user_id`,`product_id`) USING BTREE,
KEY `times` (`times`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
可以看出目前這張表是有3個索引的:
我往這張表裏面導入了10萬多條記錄。
mysql不走索引的情況
1、like查詢以“%”開頭(如果開頭、結果都有“%”,也不會使用索引,走的是全表掃描);
我這裏用了列出了4種情況,發現都是全表掃描,不走索引的。可能因爲times取值不夠離散,索引沒有走索引。
2、or語句前後沒有同時使用索引;
首先看看or語句前後同時使用索引:
查詢走索引了。
再看看or語句前後沒有同時使用索引,product_id是索引字段,server_id不是索引字段:
是沒有走索引的。
3、組合索引中不是使用第一列索引;(不符合最左匹配原則)
這張表索引爲時間time和一個組合索引:
這裏組合索引user_product,我們先使用user_id這個第一列索引,作爲查詢條件,查看執行計劃:
使用了索引。
接下來使用組合索引user_product的非第一列索引product_id,再看看執行計劃:
沒有使用索引。
4、where條件中類型爲字符串的字段沒有使用引號引起來;【查詢where條件數據類型不匹配也無法使用索引,字符串與數字比較不使用索引,因爲正則表達式不使用索引,如varchar不加單引號的話可能會自動轉換爲int型,使索引無效,產生全表掃描】
首先看看where後條件字符串正常使用引號:
使用了索引。
where後條件字符串不使用引號,而使用數字:
可以看到,查詢沒有走索引。
5、當全表掃描速度比索引速度快時,mysql會使用全表掃描,此時索引失效;(*數據量少*)
我把表數據刪了,裏面留了7條數據:
條件查詢索引字段列times:
顯然是沒有走索引的。
6、在索引字段上使用“not”,“<>”,“!=”等等;
經過驗證發現,使用這些符號後,依然會走索引。
7、對索引字段進行計算操作、使用函數;
MySql 如果表中某個時間字段(datetime/…)設置了索引,以函數 DATE_FORMAT() 爲查詢條件時,爲datetime設置的索引不生效,會引起全表掃描導致查詢很慢。
沒有用函數時候是走了索引的,查出具體到時分秒的數據:
使用函數data_format函數,查出2020-08-14這一天的所有數據:
沒有走索引,那麼怎麼解決呢?
如果一定要用函數,比如date_format,可以通過這種方式,就會走索引。
8、索引散列值(重複多)不適合建索引,例:性別、狀態等字段不適合。
不應該建立索引的字段規則
-
不應該在字段比較長的字段上建立索引,因爲會消耗大量的空間
-
對於頻繁更新、插入的字段應該少建立索引,因爲在修改和插入之後,數據庫會去維護索引,會消耗資源
-
儘量少在無用字段上建立索引【where條件中用不到的字段】
-
表記錄太少不應該創建索引
-
數據重複且分佈平均的表字段不應該創建索引【選擇性太低,例如性別、狀態、真假值等字段】
-
參與列計算的列不適合建索引【保持列"乾淨",比如from_unixtime(create_time) = '2014-05-29'就不能使用到索引,原因是b+樹中存的都是數據表中的字段值,但進行檢索時需要把所有元素都應用函數才能比較,顯然成本太大,所以語句應該寫成create_time = unix_timestamp('2014-05-29')】