系统优化---功能优化心得

需求描述

错题本功能要求展示最近一次答错的小题,如果该题最近一次答对了将不会展示,同时展示信息还包含答题总数、答错次数、连错次数及其他边缘信息

问题描述

当用户答题数据量增大的时候,应该是几千条的时候,每次查询需要5秒左右的响应时间

问题分析

首先是硬件,由于我们的产品是平板,且设备整体性能比较差;其次,pad本地用的是sqlite数据库不支持变量声明,所以不能像mysql那样比较轻松的通过变量来实现;再次,也是最主要的原因是因为编写的sql性能太差,大体思路是先进行数据全表扫描,筛选出判断每条答题记录对应该题最新的答题记录是否为错(比较绕),然后进行聚合,然后是在代码中遍历集合出来的数据,分别通过sql查询出该题对应的总记录数、答错次数、及连错次数(差sql+循环),同事该接口由于需要分页,所以上一个耗时sql又被重新调用了一次,最后导致接口特别耗时

优化分析

庆幸的是现在的我,优化接口不在只是从一个维度去考虑问题,优化是一个全局考虑的事情。硬件、架构、需求、代码、sql等都是可以优化的地方,但是以目前整体状况来看,从代码和sql上优化还是最靠谱的,因为硬件、架构和需求的优化成本都太高,而且这些都还不是问题的痛点,而且也不能体现和提升一个程序员的技术水平(不酷),最主要的是这些也不是我能决定的。

优化思路1(失败)

主要从sql出发,将数据先通过记录主键id和正确错误状态(0:错误 1正确)倒序排序,然后按小题id进行聚合,得出最近答对小题记录和没有答对过的最大错题记录,将上一个记录作为临时数据重新关联答题记录表,筛选出上一个记录中答题状态为错的记录和id大于关联id的数据(因为大于的都是在最近一次答对之后连续答错的)聚合出连错次数,答题总数和错误次数在上一次查询中已经聚合出不在描述。想想感觉应该是非常顺当的一条死路,对是一条死路,因为聚合和排序两个一块用并不能得到我们想要的结果(最起码我当时在mysql5.7上测试的是这个结果),因为是先聚合在排序,通过网上查找最终也没有解决这个问题。

优化思路2(未采用)

创建用户答题聚合表,用户答题过程中共随时更改该表中的数据,这个思路清晰简单,但是需要添加新的表,而且需要大量修改业务代码,项目本身处于一个相对稳定的状态,如果加上这一段代码后就需要重新整体测试代码,影响比较大且需要人力比价高,所以最后放弃了

优化思路3(成功)

代码+sql 首先是sql,大体思路是先聚合出每道小题的最大答题记录id,同时聚合出作答次数及错误次数,然后以这个id重新关联答题记录表同事筛选出对错状态为错的(因为上一步骤并不能聚合出最大答题记录id的同时也展示出该记录的对错状态),这一个加上分页操作(因为这些数据已经是想要的数据了,但是没有包含连错次数),然后在通过小题id关联答题记录表,得出分页之后的小题的所有答题记录,返回的肯定不是我们传入的limit的数据量,肯定比这大,那应该是怎解决呢,连错次数也没有得到,通过sql已经很费劲了因为这里面有还包含中间答对的情况,所以最后决定通过代码聚合,具体的就不在赘述了,因为不是特别难。最后终于解决了

收获

首先推翻了网上一贯流传的“尽量避免使用子查询,因为子查询性能不好”,其实好不好跟实际的业务需求是有很大关系的,比如我们产品的整体规划,产品为单机,受众用户未高考学生,所以数据量不会是几百万几千万那样,很有可能都超不过4位数,而且本身子查询在某些场景下性能还是比较好的,得益于最近看的《数据库系统概念》、《INNODB内核》及《高性能mysql》几本书,对于底层原理的了解,让我对于业务的优化有了更深刻的认识,比如其中一个名词叫做“延迟关联”在这里正好也用到了(开心),其次是,认识到任何封装在提供了易用性的同时,也带来了性能上的损耗,比如分页插件,分页插件知识简单的再业务sql的外层有加了一层统计查询来或得记录的总数,但是殊不知要获取记录的总数可以通过性能更好的sql来获取,这也是这次优化所采用的一种手段,最后就是通过java代码聚合数据,代码本身不是特别复杂,最大的收获就懂得了优化不要只从一个角度去思考

心得

这次能够优化成功且收获这么多,大部分都是得益于这半年看的书(《数据库系统概念》、《INNODB内核》及《高性能mysql》还有一本讲zk的),保持一颗不断进步的心态,才能走的更长久

你可能感兴趣的:(数据库优化)