1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 记一次mysql优化 not in not exits left join 子查询

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

时间:2024-03-02 23:49:06

相关推荐

记一次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)

SELECTSUM( bc.c_count ) AS blockNum-- ....FROMt_block_count AS bcWHEREbc.c_school_code = '****' AND bc.c_block_id NOT IN (SELECTbbd2.c_blacklist_id FROMt_block_blacklist_device AS bbd2 WHEREbbd2.c_school_code = '****' AND bbd2.c_enabled = 1 AND bbd2.c_type IN ( 1, 4 ) ) GROUP BYbc.c_block_id ORDER BYblockNum DESC;

explain结果

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

not exits方案:

SELECTSUM( bc.c_count ) AS blockNum-- ...FROMt_block_count AS bcLEFT JOIN t_block_blacklist AS bb ON bc.c_block_id = bb.c_id WHEREbc.c_school_code = '****AND NOT EXISTS (SELECTbbd2.c_blacklist_id FROMt_block_blacklist_device AS bbd2 WHEREbbd2.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 BYbc.c_block_id ORDER BYblockNum DESC;

explain结果

left join方案

简单的将其子查询部分以表的形式进行左外连接得到如下sql

SELECTSUM( bc.c_count ) AS blockNum-- ...FROMt_block_count AS bcLEFT JOIN (SELECTbbd2.c_blacklist_id FROMt_block_blacklist_device AS bbd2 WHEREbbd2.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 WHEREbc.c_school_code = '****' AND t.c_blacklist_id IS NULL GROUP BYbc.c_block_id ORDER BYblockNum DESC;

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

EXPLAIN EXTENDED -- 你的sqlSHOW WARNINGS;

注意:有两个结果,一个是explain的结果一个是show warnings的结果

根据show warings的结果再进行优化得到下面的sql

SELECTSUM( bc.c_count ) AS blockNum-- ...FROMt_block_count AS bcLEFT 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 = '****' WHEREbc.c_school_code = '****' AND bbd2.c_blacklist_id IS NULL GROUP BYbc.c_block_id ORDER BYblockNum 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

left join

not exits

想要对比他们谁的性能更优,于是我去网上查找这两者的区别,众说纷纭,有人说这个好,有人说那个好。当然网上的观点不一定是正确的,都是一些个人的理解与经验,这个时候查找书籍是最为可靠的,于是又翻了一下《高性能mysql》,书中也又一个类似的例子。以下是书中的截图

可以看到文字的尾部,书中建议是结合实际的一些测试结果来判断那种写法更好。到此,我的优化过程也结束来,我选取的是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。还有重要的是网上的观点不一定是可靠的(包括我),因此更提倡大家去查阅官方文档和书籍

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。