实操+面试题:验证mysql索引失效场景

本文主要是为了验证何种情况下索引会失效,建表使用了innoDB存储引擎
以下是一些索引失效传言,和本人验证结果,基于5.6的版本

  1. “索引列不能出现null值”,不准确,可能会全表扫描,可能会走索引,看优化器怎么衡量查询的代价
  2. “模糊查询,只有左匹配才能走索引”,对
  3. “用了or的查询,必须or两边的条件都满足最左匹配才能走索引”,对
  4. “隐式转换会引起索引失效”,对
  5. “!= 不走索引”,不知道,我在主键和非主键索引上验证了都是不走的

另外字段列值允许为null的情况下,查询时有些坑要注意:

  1. count(*)与count(具体列名)的区别:前者会将null值统计在内,后者不会,取舍要取决于null的业务含义
  2. sum(具体字段)=null的情况:当查询的结果集为空时,sum的结果为null
  3. count(1) 和 count(具体列) 是等效的,结果一致

总的来说,查询是否走索引,最终看的是mysql优化器的判断,网上很多“索引失效规律”都是有前提的

建表细节:
实操+面试题:验证mysql索引失效场景_第1张图片

-- 建表语句
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)


-- 排除name为null或者''的值
select SQL_NO_CACHE  * from t_innodb where name != '' and not(name is null)


举个栗子说明为啥是否走索引

你可能感兴趣的:(MySQL,数据库,mysql,sql)