一个慢查询引发的思考

image
今天浏览迷你喵小程序的时候意外的发现一个懵逼的问题,加载首页文章列表数据的时候,第一页很快,第二页却很慢,第三页又很快。这种象限,真的是把我整的一脸懵逼。但是事情总得解决,不能留一个定时炸弹在身边啊!

基于这种情况,我做出来一些猜想

1.会不会是因为数据库索引是昨天刚加进去的,可能索引没生效?但是仔细一想,如果索引没生效应该查询全部数据都是一样的慢,但是为了以防万一,我还是使用了sql命令,查询索引是否生效。

1
show index from `表名`;

image
结果果然索引是生效的。

这边给大家科普一下:添加索引之后,数据库已有的数据会自动添加索引,所以如果数据量大的情况下,添加索引是非常耗时的一件操作。

2.后面我又想到,会不会是nginx转发导致的问题,因为我的架构是nginx+springboot(目前只部署单台)。所以这边我做了这样一个实验,一个通过nginx来请求接口、一个通过ip+端口来进行请求。
image

结果两个请求的时间基本差不多,都在1.2s-1.3s之间,从这里可以得出结论,应该不是nginx的问题。

3.有没有可能是执行第二页的查询的时候,使索引失效,导致查询速度变慢的呢。这边我将log的日志级别修改为debug,将sql打印出来。

1
2
3
4
SELECT id, sketch, back_img, title,content, category_id, like_num, collect_num, share_num, read_num, comment_num, sort, is_sys_recommend, is_original, user_id, author_name, create_time, update_time, is_delete, version, link 
FROM article
WHERE category_id = "1" AND is_delete = 0
order by create_time DESC , id DESC LIMIT 10, 10

结果发现两个分页查询的sql一模一样,除了limit后面的参数不一样,其它都一样,所以也排除是索引失效的问题。

4.前三种假设都不成立,无奈下我只能仔细检查分页的代码,看有没有存在循环或者n+1次查询的情况出现。

1
2
3
4
5
6
7
8
9
int offset = (pageNo - 1) * pageSize;
PageInfo<ArticleDto> pageInfo = PageHelper.offsetPage(offset, pageSize)
.setOrderBy("create_time DESC , id DESC")
.doSelectPageInfo(new ISelect() {
@Override
public void doSelect() {
articleMapper.selectHomeArticles();
}
});

发现分页的代码没有任何的if逻辑上判断,所以代码层次基本可以排除,

5.所以最终定位有可能出现问题,一定是返回的数据。但是因为dao查询数据返回封装类都一样,所以只能是第二页的数据量比第三页的数据大很多,然后联想到文章表中有一个content字段,里面放置的是文章的富文本内容,数据量特别大。

我马上进行第二页和第三页的数据比对,果然第二页的富文本数据比第三页大的多,而且富文本在首页博客列表中也用不到,所以在sql中将content这个不需要的字段过滤掉就可以了。

1
2
3
4
5
6
7
<select id="selectHotArticleOrderByTypeId" resultMap="articleResultMap" parameterType="java.lang.String">
select art.id,art.sketch,art.back_img,art.title,art.category_id,art.read_num,art.collect_num,
art.author_name,art.create_time,art.update_time,
(select count(*) from article_like where article_id=art.id) as like_num,
(select count(*) from article_comment where article_id=art.id) as comment_num
from article art where art.is_delete=0 and art.category_id ='1'
</select>

果然将这个字段去掉后,接口的响应时间快了一个量级(快了10倍。。。)。

image

总结:

以后写代码的时候千万不要出现 *from的查询,如果表中的字段数据特别大的话,数据库传输的时间会非常慢。

实际开发中之所以出现这个情况,很大一部分原因是因为框架自动映射,导致你对数据返回的字段毫无感知,这也是最为致命的!

更多内容敬请关注:“林老师带你学编程”

林老师带你学编程 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!