多条件查询报表统计业务#W03

废话

入职第二天,接到了一个任务,优化报表业务....

业务需求

生成各种报表数据。 挑出其中之一来进行讨论,有一张跟进记录表,里面可以记录某人在某个时间以某种方式跟进了某个商业对象。例如,“张三”,2018.4.10,通过电话,落实了某个合同。 其中跟进方式,有电话,登门拜访等等多种方式,并支持自定义跟进方式,跟进对象也分为数种,例如某个商机,线索,合同等等。

现在需要实现,对于任意跟进者(可多个),在任意时间区间中,针对各个商业对象、跟进方式总共有多少条记录,除了总数外,还要要按照每个人,分别统计每个人对于各个商业对象、跟进方式的跟进数量。从数据库操作的角度讲,就是基于各种条件,然后count,或者先group by,再count。查询条件中,时间条件总是一个区间,跟进者条件通常是一个集合,跟进对象和跟进方式条件是一个常数。

思路

在大数据量下,这个页面性能堪忧,在分页的情况下,很多时候需要好几秒才能完成响应,一旦大量用户刷这个页面,系统风险也是非常大的。这里先不从系统的吞吐量上来考虑,只讨论如何减少单次请求的响应时间。

运用瓶颈思维来看待这个问题的话,很容易会发现请求的最大瓶颈在于数据库查询,那解决这个瓶颈的思路大概是这几种:

  • 减少DB查询的次数,所有的缓存策略都是为此而设定
  • 优化DB单次查询。优化查询语句,加索引,优化表结构,还有些是将原本数据库的操作,例如count,移到应用服务器上来做,也就是降低单点负荷,将消耗分散化。
  • 增加DB。常说的一主多从策略,将负荷分散到多个slave上来
  • 数据分区,不只是数据库层面的分区,甚至针对客户,为特定的数据量大的客户单独建立数据库,即使一点挂掉,不影响整体业务。
  • 业务层面退让。每个月定期计算出数据,供客户查阅,避免无意义的主动频繁查询,当然也会砍掉一些需求。

当然,报表业务的页面也相对复杂,各种图表,服务端渲染HTML也有一定的消耗。

解决方案

从核心成本的角度来讲,目前只先考虑上面思路中的前两种。讨论一下前两种优化的可行性。

先看缓存, 任意时间区间+任意人员,这种查询条件就将针对接口,或者说针对url的缓存方案排除在外了。每个人看到的数据都不一样,这样的缓存意义不大,命中率也会很低。

再将缓存的对象细分化。时间是变化的,但是某个人在某一天的数据记录是相对固定的,可以针对每一人,快照其每一天在各个维度上的数据。查询的时候,根据人员,提取他们在特定时间区间里面的快照数据,再对快照数据进行计算。这样几乎都直接避免了DB查询。

这种方式似乎可行。不过这些快照数据需要持久化的保存,在数据变更之后,还需要更新快照。快照的数据量也相对庞大,实现方式也比较复杂,第二次加工的计算量也非常大。 可见,不是最优的选择。

那再看看,优化单次查询。常见的策略就是,加上牛逼的索引,尽量让索引发挥查询的优势。根据业务查询条件,要让分组操作在索引下有良好的表现,必须要建立组合式的覆盖索引,防止分组的过程中回表查询。可以看到,查询条件多变且复杂,而且有集合,也有区间,这种超级索引即使有效,也会增加插入或更新的代价,在多变的需求下,维护成本也不小。

还有一个选择,专业的事情,给专业的工具去做。通过查阅资料,业界有名的ES,在业务报表统计方面的表现似乎要高出MySQL好几个量级。这是个代价小,可行度高的方式。

选择

通过上面的讨论,对于这个业务的优化,现阶段大概分为两个步骤:

  1. 将页面异步渲染,降低服务端的渲染压力。更加重要的是,基于此次修改,重构后端针对这个报表业务的查询流程,将一锅粥的逻辑,拆分为梳理查询条件、查询数据、生成json等几个独立的步骤,为下一步做准备
  2. 将报表相关的数据导入ES,将第一步重构后`查询数据`这一步交给ES来做。

很多时候,要解决问题,需要多种策略齐头并进,基于实际情况,一层层叠加,逐步实现。

下一篇文档,将针对ES的知识进行相关的学习和梳理。

猜你喜欢

转载自blog.csdn.net/u012671917/article/details/79767214
今日推荐