本文主要是为了验证何种情况下索引会失效,建表使用了innoDB存储引擎
以下是一些索引失效传言,和本人验证结果,基于5.6的版本
- “索引列不能出现null值”,不准确,可能会全表扫描,可能会走索引,看优化器怎么衡量查询的代价
- “模糊查询,只有左匹配才能走索引”,对
- “用了or的查询,必须or两边的条件都满足最左匹配才能走索引”,对
- “隐式转换会引起索引失效”,对
- “!= 不走索引”,不知道,我在主键和非主键索引上验证了都是不走的
另外字段列值允许为null的情况下,查询时有些坑要注意:
- count(*)与count(具体列名)的区别:前者会将null值统计在内,后者不会,取舍要取决于null的业务含义
- sum(具体字段)=null的情况:当查询的结果集为空时,sum的结果为null
- count(1) 和 count(具体列) 是等效的,结果一致
总的来说,查询是否走索引,最终看的是mysql优化器的判断,网上很多“索引失效规律”都是有前提的
建表细节:
-- 建表语句
CREATE TABLE `t_innodb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '是否为空对命中索引有啥影响',
`age` int(11) DEFAULT NULL,
`sex` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`) USING BTREE,
KEY `idx_age_sex` (`age`,`sex`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- 练习与验证
-- 查询mysql的安装路径
show variables like '%basedir%'
-- 查询版本号
SELECT VERSION()
-- 无法看到innoDB和myisam表的文件在磁盘上的区别
-- 测试模糊查询
-- 单个索引字段查询
EXPLAIN select * from t_innodb where age = 11
-- 范围查询也可索引
EXPLAIN select age from t_innodb where age > 11 and age <44
-- != 会导致索引失效
EXPLAIN select * from t_innodb where age != 22
-- null 是特殊的,要用is null
select * from t_innodb where age = null
-- 索引字段为null并不一定会索引失效
EXPLAIN select * from t_innodb where age is null
-- varchar字段 存了数字的话,字符转换问题会影响索引
EXPLAIN select * from t_innodb where name = '22' -- 查询结果一致,因为mysql做了隐士转换
EXPLAIN select * from t_innodb where name = 22
-- 不是的:!= 不走索引
-- select * 偶尔会影响走索引,可能是因为全表扫描的效率更高
EXPLAIN select name from t_innodb where name != '22'
EXPLAIN select * from t_innodb where name != '22'
-- 走索引
EXPLAIN select * from t_innodb where name like '22'
-- 走索引
EXPLAIN select * from t_innodb where name like '22%'
-- 右边匹配不走索引
EXPLAIN select * from t_innodb where name like '%22'
-- 查询null值不影响走索引
EXPLAIN select * from t_innodb where name is null
--
EXPLAIN select name from t_innodb where name is not null
-- 联合索引
EXPLAIN select * from t_innodb where age = 1 or age = 22 -- 晚点测试in 和 not in
-- 用了or的联合索引,无论如何一定要各自遵循最左匹配:就当做or的左右都是有隐形的括号包起来的
EXPLAIN select * from t_innodb where age = 1 or (age = 2 and sex = 22)
EXPLAIN select * from t_innodb where age = 1 and sex != 22
EXPLAIN select * from t_innodb where age = 11 and sex != 22
-- 怎么从解析计划中看到查询是否有回表操作,看extra字段
-- 关于null的一些坑
SELECT count(name) from t_innodb
SELECT count(*) from t_innodb -- 共10条
-- 含null列,查询结果不含null,
-- 所以如果对于业务而言null是有意义的话,这种写法容易查漏数据
SELECT * from t_innodb where name != 'zk'
-- 含null列,如果需要查到null
SELECT * from t_innodb where name != 'zk' or ISNULL(name)
-- sum() 对可能为null的字段使用sum
-- 没有结果集的时候返回null
SELECT sum(age) from t_innodb where id = 10
-- 直接统计的话,为null的值不参与统计
SELECT sum(age) from t_innodb
-- 结果为null
select 1 + null
select SQL_NO_CACHE * from t_innodb where name != 'zk' or ISNULL(name)
举个栗子说明为啥是否走索引