浅析索引失效(一)
先在重復一下,什么是索引?
MySQL索引的建立對于MySQL的高效運行是很重要的,索引可以大大提高MySQL的檢索速度。
但是在生產中,我們有時候會發(fā)現(xiàn),某一條sql速度慢的出奇,原因可能就是語句中的某些表示,導致了索引失效。
以下針對索引失效的幾種看法!歡迎交流!!!
一、查詢條件中包含or,可能導致索引失效。
新建一個普通表employees,
CREATE TABLE `emp` (`id` int(11) NOT NULL,`emp_no` int(11) NOT NULL,`birth_date` date NOT NULL,`first_name` varchar(14) NOT NULL,`last_name` varchar(16) NOT NULL,`gender` char(1) NOT NULL,`hire_date` date NOT NULL,PRIMARY KEY (`id`),KEY `idx_emp_no` (`emp_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;插入少量元素,意思意思?
INSERT INTO `emp` VALUES ('1','10001', '1953-09-02', 'Georgi', 'Facello', 'M', '1986-06-26'); INSERT INTO `emp` VALUES ('2','10002', '1964-06-02', 'Bezalel', 'Simmel', 'F', '1985-11-21'); INSERT INTO `emp` VALUES ('3','10003', '1959-12-03', 'Parto', 'Bamford', 'M', '1986-08-28'); INSERT INTO `emp` VALUES ('4','10004', '1954-05-01', 'Chirstian', 'Koblick', 'M', '1986-12-01'); INSERT INTO `emp` VALUES ('5','10005', '1955-01-21', 'Kyoichi', 'Maliniak', 'M', '1989-09-12'); INSERT INTO `emp` VALUES ('6','10006', '1953-04-20', 'Anneke', 'Preusig', 'F', '1989-06-02'); INSERT INTO `emp` VALUES ('7','10007', '1957-05-23', 'Tzvetan', 'Zielinski', 'F', '1989-02-10'); INSERT INTO `emp` VALUES ('8','10008', '1958-02-19', 'Saniya', 'Kalloufi', 'M', '1994-09-15'); INSERT INTO `emp` VALUES ('9','10009', '1952-04-19', 'Sumant', 'Peac', 'F', '1985-02-18'); INSERT INTO `emp` VALUES ('10','10010', '1963-06-01', 'Duangkaew', 'Piveteau', 'F', '1989-08-24'); INSERT INTO `emp` VALUES ('11','10011', '1953-11-07', 'Mary', 'Sluis', 'F', '1990-01-22');1.執(zhí)行一條sql語句,它是會走索引的,如下圖所示:
如果你沒有使用過explain這個命令來查看SQL語句的執(zhí)行計劃,這里走:https://georgedage.blog.csdn.net/article/details/103526199
或者,繼續(xù)看:
喬治大哥?
2.把or條件+沒有索引的age加上,并不會走索引,如圖所示
分析&結論:
-
對于or+沒有索引的age這種情況,假設它走了emp_no的索引,但是走到gender查詢條件時,它還得全表掃描,也就是需要三步過程:全表掃描+索引掃描+合并
-
如果它一開始就走全表掃描,直接一遍掃描就完事。
-
mysql是有優(yōu)化器的,處于效率與成本考慮,遇到or條件,讓索引失效,看起來也合情合理嘛。
注意: 如果or條件的列都加了索引,索引可能會走的,大家可以自己試一試。
二、如何字段類型是字符串,where時一定用引號括起來,否則索引失效
再創(chuàng)個表:?
CREATE TABLE `emp2` (`id` int(11) NOT NULL,`emp_no` varchar(14) NOT NULL,`birth_date` date NOT NULL,`first_name` varchar(14) NOT NULL,`last_name` varchar(16) NOT NULL,`gender` char(1) NOT NULL,`hire_date` date NOT NULL,PRIMARY KEY (`id`),KEY `idx_emp_no` (`emp_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;然后接下來看兩種情況:
分析與結論:
為什么第一條語句未加單引號就不走索引了呢?這是因為不加單引號時,是字符串跟數(shù)字的比較,它們類型不匹配,MySQL會做隱式的類型轉換,把它們轉換為浮點數(shù)再做比較。
三、like通配符可能導致索引失效。
在emp2表中,當%在前面不走索引,%在后面的話走索引。
喬治大哥?在emp表中,當%無論前后都是不走索引,這個我覺得還是想我們二中所說的在底層mysql進行了轉化,因為emp表中的emp_no是int類型。
喬治大哥然后給大家一個意想不到的:改為只查索引的字段(覆蓋索引),發(fā)現(xiàn)還是走索引
結論:
like查詢以%開頭,會導致索引失效。可以有兩種方式優(yōu)化:
-
使用覆蓋索引
-
把%放后面
附: 索引包含所有滿足查詢需要的數(shù)據(jù)的索引,稱為覆蓋索引(Covering Index)。
四、聯(lián)合索引,查詢時的條件列不是聯(lián)合索引中的第一個列,索引失效
表結構:(有一個聯(lián)合索引 idx_emp_no_age,emp_no在前, age在后)
CREATE TABLE `emp3` (`id` int(11) NOT NULL,`emp_no` int(11) NOT NULL,`age` int(11) NOT NULL,PRIMARY KEY (`id`),KEY `idx_emp_no_age` (`emp_no`,`age`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;在聯(lián)合索引中,查詢條件滿足最左匹配原則時,索引才是正常生效的。請看demo:
喬治大哥 喬治大哥 喬治大哥?
【友情補充】index這種連接類型只是另外一種形式的全表掃描,只不過它的掃描順序是按照索引的順序。這種掃描根據(jù)索引然后回表取數(shù)據(jù)
分析與結論:
-
當我們創(chuàng)建一個聯(lián)合索引的時候,如(k1,k2,k3),相當于創(chuàng)建了(k1)、(k1,k2)和(k1,k2,k3)三個索引,這就是最左匹配原則。
-
聯(lián)合索引不滿足最左原則,索引一般會失效,但是這個還跟Mysql優(yōu)化器有關的。
五、在索引列上使用mysql的內置函數(shù),索引失效。
創(chuàng)表:
CREATE TABLE `emp4` (`id` int(11) NOT NULL,`emp_no` int(11) NOT NULL,`logintime` datetime NOT NULL,PRIMARY KEY (`id`),KEY `idx_emp_no` (`emp_no`) USING BTREE,KEY `idx_login_time` (`loginTime`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;Demo:?
喬治大哥雖然loginTime加了索引,但是因為使用了mysql的內置函數(shù)Date_ADD(),索引直接GG,如圖:?
喬治大哥?
?
總結
- 上一篇: Mysql Type中的all和inde
- 下一篇: 从 Hive 大规模迁移作业到 Spar