记一次mysql优化 not in, not exits, left join 子查询

在某次业务逻辑中写到如下逻辑:查询学校级别的未拦截弹窗记录。而弹窗设备表(t_block_blacklist_device)有四种类型(1:学校级黑名单 2:设备级别黑名单 3:略 4:学校级白名单 注:从来没有被加入黑名单或白名单不存在这张表中)。弹窗弹出次数表为t_block_count。于是写下以下sql:查询弹窗弹出次数表的次数,排除掉出现在弹窗设备表中学校级的弹窗id(c_block_id中排除掉c_blacklist_id)

SELECT
	SUM( bc.c_count ) AS blockNum
	-- ....
FROM
	t_block_count AS bc
WHERE
	bc.c_school_code = '****' 
	AND bc.c_block_id NOT IN (
	SELECT
		bbd2.c_blacklist_id 
	FROM
		t_block_blacklist_device AS bbd2 
	WHERE
		bbd2.c_school_code = '****' 
		AND bbd2.c_enabled = 1 
		AND bbd2.c_type IN ( 1, 4 ) 
	) 
GROUP BY
	bc.c_block_id 
ORDER BY
	blockNum DESC;

explain结果
在这里插入图片描述
得到sql后发现not in加子查询在数据量很高的时候性能很差,于是参考网上和《高性能mysql》优化方案,写出not exits和left join的方案,并对比其性能。
not exits方案:

SELECT
	SUM( bc.c_count ) AS blockNum
	-- ...
FROM
	t_block_count AS bc
	LEFT JOIN t_block_blacklist AS bb ON bc.c_block_id = bb.c_id 
WHERE
	bc.c_school_code = '****
	AND NOT EXISTS (
	SELECT
		bbd2.c_blacklist_id 
	FROM
		t_block_blacklist_device AS bbd2 
	WHERE
		bbd2.c_school_code = '****
		AND bbd2.c_enabled = 1 
		AND bbd2.c_type IN ( 1, 4 ) 
		AND bc.c_block_id = bbd2.c_blacklist_id
	) 
GROUP BY
	bc.c_block_id 
ORDER BY
	blockNum DESC;

explain结果
在这里插入图片描述
left join方案
简单的将其子查询部分以表的形式进行左外连接得到如下sql

SELECT
	SUM( bc.c_count ) AS blockNum
	-- ...
FROM
	t_block_count AS bc
	LEFT JOIN (
	SELECT
		bbd2.c_blacklist_id 
	FROM
		t_block_blacklist_device AS bbd2 
	WHERE
		bbd2.c_school_code = '****' 
		AND bbd2.c_enabled = 1 
		AND bbd2.c_type IN ( 1, 4 ) 
	) AS t ON t.c_blacklist_id = bc.c_block_id 
WHERE
	bc.c_school_code = '****' 
	AND t.c_blacklist_id IS NULL 
GROUP BY
	bc.c_block_id 
ORDER BY
	blockNum DESC;

执行SHOW WARNINGS的结果;查看一下系统的优化

EXPLAIN EXTENDED 
-- 你的sql
SHOW WARNINGS;

注意:有两个结果,一个是explain的结果一个是show warnings的结果
在这里插入图片描述
根据show warings的结果再进行优化得到下面的sql

SELECT
	SUM( bc.c_count ) AS blockNum
	-- ...
FROM
	t_block_count AS bc
	LEFT JOIN t_block_blacklist_device AS bbd2 ON bbd2.c_blacklist_id = bc.c_block_id 
	AND bbd2.c_enabled = 1 
	AND bbd2.c_type IN ( 1, 4 ) 
	AND bbd2.c_school_code = '****' 
WHERE
	bc.c_school_code = '****' 
	AND bbd2.c_blacklist_id IS NULL 
GROUP BY
	bc.c_block_id 
ORDER BY
	blockNum DESC

瞬间sql就变得简洁多了
explain的结果:
在这里插入图片描述
分析:
当数据量多的时候,我们发现left join 和not exits可以用到idx_blacklist_id和idx_school_code_enabled,可以看到优先用的是idx_blacklist_id这个索引,因为这个索引比idx_school_code_enabled的辨识度更大,性能也相对好一些,数据量大的时候更加明显。而not in中只能使用到idx_school_code_enabled。因此单从索引的使用上not in的性能就比这两者差。
我们可以看到left join 和not exits这两者explain的结果差不多,区别在t_block_blacklist_device表的select_type和extra不太,我们把它放一起会更加清楚
left join
在这里插入图片描述
not exits
记一次mysql优化 not in, not exits, left join 子查询_第1张图片
left join
在这里插入图片描述
not exits
在这里插入图片描述
想要对比他们谁的性能更优,于是我去网上查找这两者的区别,众说纷纭,有人说这个好,有人说那个好。当然网上的观点不一定是正确的,都是一些个人的理解与经验,这个时候查找书籍是最为可靠的,于是又翻了一下《高性能mysql》,书中也又一个类似的例子。以下是书中的截图
记一次mysql优化 not in, not exits, left join 子查询_第2张图片
可以看到文字的尾部,书中建议是结合实际的一些测试结果来判断那种写法更好。到此,我的优化过程也结束来,我选取的是left join的方式来作为我的sql,在现实生活中我们往往不需要对性能要求得那么吹毛求疵,因此我采用来更加简介的方式来写这个sql(或许你觉得not exits更加易读,也可以用这种方式)。

结论:以上的过程是我结合具体业务写的sql,并不代表not exits, left join的性能就一定比not in好,not exits和left join性能就差不多。据网上查阅的资料说:mysql官方文档中又提到在一些场景下,这两者mysql自己的优化结果是相同的(当然,我还没查找到相关资料)。因此建议遇见sql优化时,不要用绝对的态度去优化,而应该explain分析一下。也可以用show warings查看mysql优化后执行的真实sql。还有重要的是网上的观点不一定是可靠的(包括我),因此更提倡大家去查阅官方文档和书籍

你可能感兴趣的:(mysql,mysql,not,in,not,exits,left,join,子查询优化)