合理地對數據表加索引可以大大加快數據的訪問效率,但索引也不是對任何查詢都有效,如果sql的結構不當,也會發生索引失效。所以爲避免採坑,寫下此文,作爲記錄。下面的例子是在mysql中創建的一張表,建表語句如下:
CREATE TABLE `user_info` (
`id` bigint(32) NOT NULL,
`name` varchar(32) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`address` varchar(32) DEFAULT NULL,
`nick_name` varchar(32) DEFAULT NULL,
`role_id` bigint(32) DEFAULT NULL,
`create_time` date DEFAULT NULL,
`update_time` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
一、儘量選擇複合索引
在對錶創建索引時,如果可以使用複合索引,儘量使用複合索引而不是單列索引,理由如下。現創建的一個複合索引:
CREATE INDEX idx_name_age_status ON user(name, age, status);
這裏創建了一個名爲idx_name_age_status的複合索引,索引涉及到三個字段。在mysql中,這一個索引實際上相當於三個索引,分別是name、 name + age、 name + age + stauts,那麼在使用name或name + age或name + age + stauts進行查詢時,都會使索引生效。而如果分別給三個字段三個單列索引,在查詢這張表時條件中有這三個字段,數據庫會選擇一個最優的單列索引,而不會使用全部三個索引。
二、最左前綴法
使用到複合索引時,注意最左前綴法,也就是查詢的條件需要從索引的最左列開始,並且不跳過中間列,才能使索引發揮最好的效果。現在創建這樣的索引:
CREATE INDEX idx_name_age_status ON user(name, age, status);
使用name或name + age或name + age + status進行查詢能使索引生效;使用單個查詢條件age或status或者age + status 進行查詢不會使索引生效;使用name + stauts進行查詢只有name字段的索引生效。
三、使用複合索引時範圍查詢右邊的列索引不會生效
三個字段在同一個複合索引中,如果查詢條件中用到了範圍查詢,範圍查詢後面的索引不再生效。比如創建如下索引:
CREATE INDEX idx_name_age_status ON user(name, age, status);
使用這三個字段作爲查詢條件,處於中間的age如果用到了範圍查詢,則status上的索引不會生效。
-- name + age + nick_name索引生效
SELECT * FROM user_info WHERE name = '張三' AND age = 22 AND nick_name = '三兒';
-- age用到了範圍查詢,僅name + age索引生效
SELECT * FROM user_info WHERE name = '張三' AND age > 22 AND nick_name = '三兒';
四、在索引列上進行運算操作後,索引將失效
在索引列上進行運算操作後,索引將失效。比如有這樣一個索引:
CREATE INDEX idx_name ON user_info(name);
直接以name作爲條件查詢索引是生效的,如果在name上使用函數會導致索引失效,比如這樣:
SELECT * FROM user_info WHERE substring(name, 1, 2) = '夏侯';
五、模糊匹配不生效的情況
查詢條件中的模糊條件,如果是以%開頭,不走索引,比如
SELECT * FROM user_info WHERE name like '%三兒%'; -- 不走索引
或者
SELECT * FROM user_info WHERE name like '%三兒'; -- 不走索引
以上兩種寫法都不會走索引,下面這種寫法索引是有效的。
SELECT * FROM user_info WHERE name like '三兒%'; -- 走索引
六、字符串沒加引號,索引失效
查詢時字符串如果沒加引號,索引失效。
在mysql中,假設有一個字段名是name,類型爲VARCHAR,使用name作爲條件查詢。此時忘記給name的值加上引號,就像這樣:
SELECT * FROM user_info WHERE name = 12; -- name爲VARCHAR類型
此時name條件查詢的值是12,mysql是可以執行的,如果這裏寫一個name = 張三,mysql不能執行。雖然這條語句可以執行,並且如果數據表中有name值爲12的數據也是可以查出來的。但是,它不會走索引。
七、作爲條件的索引字段位於or兩側,導致該索引失效
name字段上有索引,所以這樣查肯定是走索引的。
SELECT * FROM user_info WHERE name = '張三';
在這張表中另外有一列nick_name,不管這一字段有沒有加索引,下面這一個語句都不會走索引。
SELECT * FROM user_info WHERE name = '張三' OR nick_name = '頑皮';
八、儘量不使用SELECT *,最好只查詢索引列
儘量使用覆蓋索引,也就是儘量只查詢索引列,比如複合索引如下:
CREATE INDEX idx_name_age_status ON user(name, age, status);
使用SELECT * FROM user_info WHERE name = '張三' AND age = 12 AND status = 1進行查詢,索引自然會生效,但是效率肯定不如SELECT name, age, status FROM user_info WHERE name = '張三' AND age = 12 AND status = 1,這在數據量大的時候可以看出來。
另外,儘量不要寫SELECT * FROM user_info這樣的語句,SELECT後面只跟需要用到的列是好的選擇。