在软件项目中,用户常常会抱怨报表查询特别慢,甚至慢的不能忍受。这给开发人员带来巨大的压力。那么,报表的处理过程如何划分?处理过程中有哪些环节容易出现性能问题?如何优化这些环节呢?

    我们先看一下报表处理过程中的各个环节划分,如下图:

2014-06-20_090815.jpg

   如图所示,报表处理过程为:
    1、用户选择报表输入参数后,报表引擎会根据报表模板和输入参数来解释报表,并将数据计算和读取请求发送给数据层。

    2、数据层负责读取、计算和返回数据。数据层一般都是传统数据库:Oracle、DB2等。

    3、数据层通过JDBC等接口将结果数据传输给报表工具。

    4、报表工具将接收到的数据,按照报表模板的样式生成可视化报表展现给用户。

    上述这些报表处理过程中,网络、报表层和数据层的各个环节都有可能成为性能的瓶颈。

    需要指出的是图1中“R2生成可视化报表”也会包含一些数据计算——从数据层返回的数据可能要经过进一步加工才能满足报表的要求。对具备编程能力的报表工具来说,这个环节会消耗一些计算时间,比如BIRT的自定义脚本、润乾报表的自定义数据集、格间计算等。

    因此,我们可以把这个环节分开,更有利于分析影响报表性能的因素。分开后如下图:

esproc_report_performance2.jpg

   在图2中***部分是影响报表性能的主要因素。也就说,报表引擎对报表的解释和生成可视化报表的环节一般都不会成为性能问题的主要因素,最容易造成报表慢的,是数据计算和JDBC传输。因此,针对数据计算和JDBC传输来进行优化能够事半功倍。

    分析一下图2中***部分影响性能的原因:

    一、 数据层“D1执行数据计算”环节

    在实际项目中,报表开发人员往往需要编写一些很长很复杂的SQL语句(或存储过程),来为报表计算数据。

    报表开发人员会发现,使用SQL(或存储过程)实现复杂统计运算非常困难,特别是与序和集合有关的运算,如计算每月销售额都排进前10名的产品、连续五天上涨的股票等,为报表准备数据的SQL过于复杂,可能长达数十行甚至数百行。

    因为难以完整指定数据库的执行路径,所以复杂SQL执行起来一般都比较慢。报表开发人员想人工优化复杂SQL的时候也很难入手。这是“D1执行数据计算”环节影响报表性能的主要因素。

    例子:计算每个月的销售额都在前10名的客户。 

        select Client from(

          select * from(

          select B.*,row_number() over(partition by month order by SumValuedesc)

          rown  from(

          selectto_char(SellDate,’mm’) month,Client,sum(Quantity*Amount) SumValue

          from contract

          whereSellDate>=to_date(’2012-01-01′,’yyyy-mm-dd’)

          andSellDate<=to_date(’2012-12-31′,’yyyy-mm-dd’)

          group by to_char(SellDate,’mm’),Client order by month,client ) B

          )C

          whererown<=10

          )D

          group by Client

          having count(Client)=(

          select  count(distinct(to_char(SellDate,’mm’)))

          from contract

         whereSellDate>=to_date(’2012-01-01′,’yyyy-mm-dd’)

          andSellDate<=to_date(’2012-12-31′,’yyyy-mm-dd’)

        )

    二、数据层“D2 JDBC传输数据”环节

    传统主流数据库提供的JDBC驱动在将数据流转换成Java对象时的效率很低,但又不可避开,在大量数据时就会感觉速度非常慢。虽然数据库的版本不断升级,但是这个问题一直都没有解决好。所以,对于较大源数据量报表来说,JDBC经常也是报表变慢的主要因素。

    三、报表层“R2数据再次计算”环节
    1、 很多报表工具会把“R2数据再次计算”和“R3生成可视化”两个环节一并处理,数据计算的时候附带着很多显示属性(字体、颜色、格子大小等等),会一定程度上降低计算速度。

    2、 报表工具计算程序,一般是自定义函数、javascript或者java语言,并不提供完备的关系数据计算类库,很多功能需要程序员自行编写实现。结果是:一方面程序员编写成本较高、时间较长;另一方面,也造成代码质量层次不齐,很难保证良好的性能。

    3、 报表工具不具备并行计算的能力,大数据量复杂任务无法拆成单机、多机的并行小任务,很难有效的提高计算速度。

    知道了造成这几个环节性呢过问题的原因,可以考虑如下几方面的优化:

    一、数据层“D1执行数据计算”环节
    针对复杂SQL很难优化的问题,可以考虑采用过程化的编程语言。这方面,集算器(esProc)就是一个很好的方案,其代码就更符合自然思维习惯,解决同样问题会比SQL短数倍。一方面,易于开发、调试和维护,降低了开发成本;另一方面,由程序员决定计算执行过程和路径,更易于提高计算性能。

    每个月的销售额都在前10名的客户的例子用集算器来实现就非常简明:

esproc_report_performance3.jpg

    二、 数据层“D2 JDBC传输数据”环节
    针对传统数据库JDBC传输缓慢的问题,可以考虑将较大规模的历史数据存在文件系统中。利用集算器来访问文件系统中的数据,要比传统数据库的JDBC数据传输的速度快很多。集算器对报表工具提供数据虽然也是JDBC方式,但集算器JDBC不需要再做数据流的对象转换,要比传统数据库快很多。

    另一个可以考虑的解决办法是,利用集算器并行机制,同时用多个子程序通过不同的JDBC连接同时从数据库中取数。实际对oracle数据库的测试发现,16个并行任务同时取数的时候,速度可以提高十倍。考虑到数据库压力增加的问题,这个解决办法适用于本身负载压力较小的情况。

    三、报表层“R2数据再次计算”环节
    1、集算器不附带显示属性,有效提高计算速度。

    2、集算器具有关系数据计算的完备而且丰富的类库,提供了大量提高性能的优化选项。特别是与序和集合有关的运算,程序员可以有效利用数据在业务上的规律,控制执行路径,提高计算速度。

    3、集算器还具备并行计算能力,可以把大数据量复杂任务拆成多机的并行小任务,有效的提高计算速度。

注:性能测试指标请参见集算器性能测试报告