查询器的查询负优化?
慢查询
最近在完成一个项目的三期工作,很是艰辛。
前两期历史遗留了许多慢查询,但由于过于久远,团队一直没有合适的时间去再回顾一遍业务逻辑,趁着这次三期工作,也想着顺便干掉之前的慢查询。
主要是针对下表:
1 | CREATE TABLE `demo` ( |
分析
有这么一句sql(涉及敏感数据均用’ב代替):
1 | SELECT `id`, `org_id`, `date`, `survey_id`, `send_num`, `reply_num`, |
其执行时间足足有829ms,当org_id是in操作的时候,可以达到2000ms+。
肯定有人第一直觉,觉得是SUM函数计算过多,导致慢查询。首先,这里并不会介绍其中的业务逻辑,但是这么多SUM运算都是有意义的;其次,这并不是导致慢查询的原因,把SUM全部去掉,执行时间还是有800ms+。
一眼看不出优化策略的,我们可以试着explain一下它:
根据索引最左匹配原则,为什么没有命中idx_date_survey_org这个联合索引呢?在sql的实际执行过程,MySQL查询优化器会根据实际的情况选择索引,之所以在这里不命中idx_date_survey_org是因为date检索的时间跨度比较大,检索数据多,不如查询优化器选择的索引。
那么查询优化器选择的索引又是什么呢?可以看到在这一条sql的执行过程中竟然用到了两个索引,并且进行了一个叫index_merge的东西。
index_merge
什么是index_merge?查阅资料说明如下:
MySQL5.0之前,一个表一次只能使用一个索引,无法同时使用多个索引分别进行条件扫描。但是从5.1开始,引入了 index merge 优化技术,对同一个表可以使用多个索引分别进行条件扫描。
原来是这样,所以说这是MySQL针对查询的优化才对…吗?
这里不得不介绍一下survey_id这一列的特殊性:这个字段总共有三个值,分别对应每一期。也就是说合适的时间跨度下,survey_id的值是一样的。而全表200w+的数据,如果要对此索引进行检索,真的有必要吗?
稍微了解过索引优化的就会知道,这当然是没有必要的。不仅没有必要,这还会拖慢整个sql的查询。因为index_merge必须等待survey_id检索完,再进行intersect(此sql用到的合并算法)。
优化
如此一来,优化策略就很简单粗暴了,有以下三种:
- 强制使用其一索引
- 直接干掉where中的survey_id
- 联系dba干掉index_merge
最后一种过于粗暴,不建议使用(因为这可能会被dba干掉)
强制索引
优化后的语句如下:
1 | SELECT `id`, `org_id`, `date`, `survey_id`, `send_num`, `reply_num`, |
执行耗时20ms,explain结果:
去掉对应检索项
由于业务方面的考虑,没有对前几期survey_id检索的必要,顾项目中使用了这种方案。
优化后的语句如下:
1 | SELECT `id`, `org_id`, `date`, `survey_id`, `send_num`, `reply_num`, |
执行耗时38ms,explain结果: