Mysql中IS NULL、IS NOT NULL不能走索引?

关于MySQL查询条件中的IS NULL和IS NOT NULL不能使用索引的说法,实则不然。文章通过实验验证了IS NULL和IS NOT NULL在一定条件下可以走索引。B+树的结构解释了为何NULL值在索引中会被放在最左边。索引是否生效取决于查询成本,当NULL值占比高时,IS NULL可能不走索引。此外,文中探讨了LIKE '%字符串%'情况下的索引使用策略,指出在特定情况下,即使包含通配符,索引也可能被利用。

mysql中IS NULL、IS NOT NULL不能走索引?

不知道是啥原因也不知道啥时候, 江湖上流传着这么一个说法 mysql查询条件包含IS NULL、IS NOT NULL、!=、like %* 、like %*%, 不能 使用索引查询, 只能 使用全表扫描。

刚入行时我也是这么认为的,还奉为真理!

但是时间工作中你会发现还是走索引啊!下面我们来一一探究其中的奥秘。

一、首先验证一下是会走索引的

创建一个表,结构如下:

create table user_info( 
    
      id int PRIMARY key auto_increment, 
    
      name varchar(16) default '', 
    
      age tinyint default 0, 
    
      address varchar(32) default '', 
    
      PRIMARY KEY (`id`), 
    
      KEY `name` (`name`), 
    
      KEY `address_2` (`address`,`name`) 
    
      ); 
    
      ALTER TABLE user_info ADD INDEX (NAME); 
    
      ALTER TABLE user_info ADD INDEX (address);
数据1 
    
      INSERT INTO user_info(NAME,age,address) 
    
      VALUES (9,9,'shenzhen9'); 
    
      BEGIN 
    
      DECLARE i INT DEFAULT 1000; 
    
      WHILE i < 9000 DO 
    
      INSERT INTO user_info (`NAME`, `age`, `address`) 
    
      VALUES 
    
      (NULL, i , SUBSTRING(MD5(RAND()),1,10) ) ; 
    
      SET i = i+ 1 ; 
    
      END WHILE ; 
    
      ① EXPLAIN SELECT * FROM user_info WHERE `name` IS NOT NULL 
    
      ② EXPLAIN SELECT * FROM user_info WHERE `name` !='9' 
    
      ③ EXPLAIN SELECT * FROM user_info WHERE `name` is null
数据2 
    
      INSERT INTO user_info(NAME,age,address) 
    
      VALUES (null,9,'shenzhen9'); 
    
      BEGIN 
    
      DECLARE i INT DEFAULT 1000; 
    
      WHILE i < 9000 DO 
    
      INSERT INTO user_info (`NAME`, `age`, `address`) 
    
      VALUES 
    
      (REPLACE(UUID(),'-',''), i , SUBSTRING(MD5(RAND()),1,10) ) ; 
    
      SET i = i+ 1 ; 
    
      END WHILE ; 
    
      ④ EXPLAIN SELECT * FROM user_info WHERE `name` IS NOT NULL 
    
      ⑤ EXPLAIN SELECT * FROM user_info WHERE `name` !='9' 
    
      ⑥ EXPLAIN SELECT * FROM user_info WHERE `name` is null

执行数据1 会发现sql①②走索引,③不走索引

执行数据2 会发现sql⑥走索引,④⑤不走索引

技术分享 || Mysql中IS NULL、IS NOT NULL不能走索引?

 

技术分享 || Mysql中IS NULL、IS NOT NULL不能走索引?

 

二、B+树数据排列规则

1、聚簇索引索引:

①页面中的记录是按照主键值进行排序的;

②B+树每一层节点(页面)都是按照页中记录的主键值大小进行排序的;

③B+树叶子节点对应的页面中存储的是完整的用户记录(就是一条记录中包含我们定义的所有列值,还包含一些InnoDB自己添加的一些隐藏列);

2、二级索引:

①页面中的记录是按照给定的索引列的值进行排序的。

②B+树每一层节点(页面)都是按照页中记录的给定的索引列的值进行排序的。

③B+树叶子节点对应的页面中存储的只是索引列的值 + 主键值。

二级索引值能为空。那对于索引列值为NULL的二级索引记录,在B+树的哪个位置呢?

在B+树的最左边。如下图

技术分享 || Mysql中IS NULL、IS NOT NULL不能走索引?

 

至于为什么,InnoDB是这样的规定:SQL中的NULL值是列中最小的值

什么时候索引又不生效了呢?

对比数据1和数据2两个数据中null值的数量不一样,当null值占多数时is not null 和!=走索引 ,is null不走索引了,数据2刚好相反。

索引(二级索引)扫描成本:

1、读取索引记录成本

2、反查主键索引查找完整数据成本即回表

如果查询读取的二级索引越多那么需要回表查询的次数就会越多,达到一定的比例就会变成全部查询了,也就是上面null 查询时索引有时不生效的原因。

综上MySQL中决定使不使用某个索引执行查询的依据是成本大小。而不是在WHERE子句中用了IS NULL、IS NOT NULL、!=这些条件

三、如何让like‘%字符串%’,‘字符串%’时走索引

通常情况下我们使用like % %、% 的确不会走索引 但是并不代表就一定不能走索引,我们对上面表中name和age建立复合索引

explain select name from user_info where name like '%a%'; 
    
      1 SIMPLE user_info index idx_n_a 53 6 16.67 Using where; Using index 
    
      explain select name,age from user_info where name like '%a%'; 
    
      1 SIMPLE user_info index idx_n_a 53 6 16.67 Using where; Using index 
    
      以下两个例子是查询了不在复合索引中的列进而造成全表扫描 
    
      explain select name,age,address from user_info where name like '%a%'; 
    
      1 SIMPLE user_info ALL 6 16.67 Using where 
    
      explain select * from user_info where name like '%a%'; 
    
      1 SIMPLE user_info ALL 6 16.67 Using where

所以like走不走索引并不是绝对的,要看使用条件!

<think>嗯,用户这次的问题是关于MySQL中使用IS NOT NULL进行索引非空判断的优化方法。我需要结合提供的四个引用内容来给出专业建议。 用户已经查阅过相关资料,但网上存在相互矛盾的说法(比如引用2就指出很多人错误认为IS NULL会导致索引失效),说明用户需要权威的澄清和实操建议。从引用3和4来看,核心问题其实是查询优化器的成本计算——当使用IS NOT NULL时是否索引取决于回表数量。 让我梳理关键点:首先是字段属性,引用1提到如果字段声明为NOT NULLMySQL会直接优化掉IS NULL条件,这对IS NOT NULL也有参考价值。然后引用4解释了失效本质是回表成本过高,临界值在10-30%左右。这提示优化方向应该是控制扫描范围。 具体优化方法可以分几个层面:第一是索引设计,联合索引要把IS NOT NULL字段放最后避免中断匹配(引用4);第二是结合LIMIT降低影响(引用3);第三是利用覆盖索引避免回表(引用4)。另外字段的NOT NULL约束也很重要(引用1),过用户明确问的是IS NOT NULL场景。 用户可能还想知道如何确认优化效果。引用2作者提到亲自测试的重要性,所以应该建议用EXPLAIN验证。考虑到用户研究的是索引优化,大概率是开发或DBA角色,可以给出进阶建议比如监控Handler_read_next值。 突然想到个隐患:如果用户在NULL值很多的字段用IS NOT NULL,即使索引效率也可能低下。这点需要提醒,但原引用没直接说明,我根据成本原理补充在注意事项里比较稳妥。 </think>针对MySQL中使用`IS NOT NULL`进行索引优化的方法,结合引用内容的核心结论和实践建议,整理优化策略如下: --- ### **核心优化原则** MySQL执行`IS NOT NULL`时**是否索引取决于数据分布和查询成本**(引用3、4): > **优化器决策逻辑**: > - 当通过索引过滤后仍需回表查询大量行(通常 > 10%~30%),优化器可能放弃索引选择全表扫描。 > - `IS NOT NULL`本身**直接导致索引失效**,失效主因是**回表成本过高**(引用2、4)。 --- ### **具体优化方法** #### 1. **索引设计优化** - **联合索引顺序** 若查询包含多个条件,将`IS NOT NULL`字段放在联合索引**末尾**: ```sql -- 推荐写法(假设联合索引为 (status, created_at)) SELECT * FROM orders WHERE status = 'shipped' AND created_at IS NOT NULL; -- created_at 在索引末尾 ``` - **覆盖索引**(引用4) 避免回表查询,将查询字段全部纳入索引: ```sql -- 创建覆盖索引 ALTER TABLE users ADD INDEX idx_email_name (email, name); -- 查询只需索引字段 SELECT email, name FROM users WHERE email IS NOT NULL; -- 无需回表 ``` #### 2. **查询语句优化** - **控制扫描范围**(引用3、4) 添加可缩小结果集的过滤条件,降低回表行数: ```sql SELECT * FROM products WHERE category_id = 5 -- 先通过索引过滤大部分数据 AND stock IS NOT NULL; ``` - **使用`LIMIT`** 限制结果数量,促使优化器选择索引: ```sql SELECT * FROM logs WHERE user_id IS NOT NULL LIMIT 100; ``` #### 3. **表结构优化** - **声明`NOT NULL`约束**(引用1) 若字段应禁止`NULL`值,直接声明为`NOT NULL`: ```sql ALTER TABLE employees MODIFY COLUMN phone VARCHAR(20) NOT NULL; ``` > ✅ 优化器将跳过`NULL`检查,直接使用索引。 - **减少`NULL`值比例** 通过默认值替代`NULL`(如空字符串、0等),降低索引扫描成本。 --- ### **验证索引效果** 使用`EXPLAIN`确认执行计划(引用2、3): ```sql EXPLAIN SELECT * FROM customers WHERE address IS NOT NULL; ``` 关键输出项: - `type`: `range`(范围扫描)或 `ref`(索引访问)表示使用索引 - `Extra`: `Using index`(覆盖索引)或 `Using where`(索引过滤后回表) --- ### **注意事项** 1. **数据分布影响** - 若字段中`NULL`值极少(如 < 5%),`IS NOT NULL`通常高效索引。 - 若`NULL`值占比高(如 > 30%),需结合其他条件优化扫描范围(引用3、4)。 > 💡 **案例**:用户表有100万行,`last_login`字段90%为`NULL`。查询`WHERE last_login IS NOT NULL`可能全表扫描,添加`AND last_login > '2023-01-01'`可触发索引。 2. **避免过度依赖网传结论** - MySQL版本、数据量、索引类型均影响优化器决策(引用2),需实际验证。 --- ### **总结建议** | **场景** | **优化策略** | |-------------------------|----------------------------------| | 高频查询`IS NOT NULL` | 创建覆盖索引 + 联合索引末尾放置 | | 表中`NULL`值占比高 | 添加辅助条件缩小范围 + `LIMIT` | | 字段逻辑上禁止`NULL` | 声明为`NOT NULL`约束 | > 通过控制回表行数、合理设计索引和表结构,可显著提升`IS NOT NULL`的查询效率(引用4)[^4]。实际执行计划请务必用`EXPLAIN`验证[^2][^3]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值