相关文章推荐

MySQL慢查询优化之IN查询优化

在生产环境中使用了in查询,in查询内部嵌套了子查询,in所在的键上已经创建了索引,但是在生产环境中却没有命中索引,并且该表数据量不到一百万,查询居然耗时10s以上。笔者在开发环境创建了一些表和数据并复现了慢查询(表和数据与公司业务无关),其中tb_book_main是书籍主体表,记录了书的作者,翻译,出版社等信息,其中isbn列建有索引,tb_book_base是书籍基本信息表。

explain SELECT
	`isbn`,
	`code`
	tb_book_main
WHERE
	`isbn` IN ( SELECT `isbn` FROM tb_book_base WHERE `publish_time` BETWEEN 20191003 AND 20191103 )
	AND `role` = 100
LIMIT 1000,10

使用explain查看索引命中情况,发现并没有使用isbn索引

笔者继续使用force index强制使用isbn索引,发现索引仍然没有命中

除了没有命中索引,更致命的是sql涉及到了dependent subquery,这意味着每扫描一行tb_book_main都要执行一次IN内部的子查询,这样的查询语句,即使命中了索引,速度也会相当慢。为了解决IN慢查询问题,笔者想到了如下两个方案

拆分sql语句

笔者怀疑生产环境5.5版本的mysql in查询就是无法命中索引,于是去除子查询进行验证,发现in在没有子查询的情况下是可以利用索引的,explain如下

于是笔者有了第一种方案,这种方案是拆分sql子查询,将一条sql拆为两条sql,将in内部的sql拆分出来,取出isbn集合列表,然后利用mybatis的sql拼接的功能,拼成完整的sql语句。Xml代码如下所示,第一步首先利用时间参数分页取出isbn列表

<select id="findIsbnBetweenTime" >
    SELECT `isbn` FROM `tb_book_base` WHERE publish_time BETWEEN #{fromTime} AND #{toTime} LIMIT #{offset},#{size}
</select>

第二步先在服务层中判断上一轮取出的isbn列表是否为空,如果不为空继续向下执行,如果isbn集合列表不为空,继续利用mybatis提供的xml动态标签功能,拼接出in查询条件

<select id="findBookMain" resultType="com.lantian.db.model.BookMain">
   SELECT * FROM `tb_book_main` WHERE `isbn` IN (
    <foreach collection="list" item="isbn" separator=",">
        #{isbn}
    </foreach>
</select>

联结代替子查询

这种方案原理还是避免子查询,将子查询语句改写为联结查询,改写后的sql语句如下所示

SELECT
	t1.`isbn`,
	`code` 
 
推荐文章