Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

0%

# 聊聊 mysql 索引的一些细节

发表于 2020-12-27 分类于 Mysql ， C ， 索引 ， Mysql 阅读次数： 阅读次数： Disqus：

前几天同事问了我个 mysql 索引的问题，虽然大概知道，但是还是想来实践下，就是 is null，is not null 这类查询是否能用索引，可能之前有些网上的文章说都是不能用索引，但是其实不是，我们来看个小试验

    CREATE TABLE `null_index_t` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `null_key` varchar(255) DEFAULT NULL,
      `null_key1` varchar(255) DEFAULT NULL,
      `null_key2` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `idx_1` (`null_key`) USING BTREE,
      KEY `idx_2` (`null_key1`) USING BTREE,
      KEY `idx_3` (`null_key2`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

用个存储过程来插入数据

    delimiter $	#以delimiter来标记用$表示存储过程结束
    create procedure nullIndex1()
    begin
    declare i int;
    declare j int;
    set i=1;
    set j=1;
    while(i<=100) do
    	while(j<=100) do
    		IF (i % 3 = 0) THEN
    	     INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (null , LEFT(MD5(RAND()), 8), LEFT(MD5(RAND()), 8));
        ELSEIF (i % 3 = 1) THEN
    			 INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (LEFT(MD5(RAND()), 8), NULL, LEFT(MD5(RAND()), 8));
    	  ELSE
    			 INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (LEFT(MD5(RAND()), 8), LEFT(MD5(RAND()), 8), NULL);
        END IF;
    		set j=j+1;
    	end while;
    	set i=i+1;
    	set j=1;
    end while;
    end
    $
    call nullIndex1();

然后看下我们的 is null 查询

    EXPLAIN select * from null_index_t WHERE null_key is null;

再来看看另一个

    EXPLAIN select * from null_index_t WHERE null_key is not null;

从这里能看出来啥呢，可以思考下

从上面可以发现，`is null`应该是用上了索引了，所以至少不是一刀切不能用，但是看着`is not null`好像不太行额
我们在做一点小改动，把这个表里的数据改成 9100 条是 null，剩下 900 条是有值的，然后再执行下
然后再来看看执行结果

    EXPLAIN select * from null_index_t WHERE null_key is null;

    EXPLAIN select * from null_index_t WHERE null_key is not null;

是不是不一样了，这里再补充下我试验使用的 mysql 是 5.7 的，不保证在其他版本的一致性，
其实可以看出随着数据量的变化，mysql 会不会使用索引是会变化的，不是说 is not null 一定会使用，也不是一定不会使用，而是优化器会根据查询成本做个预判，这个预判尽可能会减小查询成本，主要包括回表啥的，但是也不一定完全准确。