横向多源关联分片性能分析
信息系统中,数据往往分散在一个库或者多个库中,利用统计报表对数据进行展现时,就经常需要从多个库中读取数据,在一张表里进行统计和展现。
如果数据存在于多个库中,那么数据只能取到报表中进行数据关联和运算,如果数据存在于一个库的多张表里,有时候可以通过写sql或者存储过程进行关联,但是更多时候为了加快运算速度,也需要取到报表中进行数据关联。
从多个库或者多张表中分别取数,然后在报表中进行关联的报表,称为多源关联报表。
多源往往带来分片,正是由于分片,使得报表设计必须直接基于多源进行,而不能先将多源转成单源进行。有相当一部分分片报表无论如何也不可能换成单源处理,部分能转成单源的报表处理也非常繁琐。
分片是指报表的纵向或横向或双向同时被分成了多个区域,每个区域扩展重复规则不同,而又可能相互运算。
如下表就是一个较典型的固定列横向分片报表。
这个报表中涉及到的人员基本信息、收入信息、考勤信息、费用情况一般来讲在数据库中会分别存在不同的数据表中。由于传统工具是单数据源的,为了组织出这个报表需要的数据,就必须在数据源设计阶段把这四个数据表叉乘起来(JOIN),且不论写个四表叉乘的SQL有多麻烦(无论图形化设计还是手写都不会简单,WHERE子句中会有太多的AND),我们只考虑这样一句复杂的SQL的运算效率会怎样。
数据表叉乘的运算复杂度是各表记录数的乘积,即N1*N2*…*Nk。k是分片数,Ni是第i个数据表的记录数,简单地写,就是指数级的O(Nk)复杂度,分片数少时感觉不明显,若k较大时(比如分个七八片、十几片),这个运算量会急剧上升,到了不可容忍的地步,再优秀的数据库都会被这种运算拖垮。
博计报表的处理方法则完全不同,由于可以直接支持多数据源,上面的报表就不需要写成一句四表叉乘的SQL,而是写四句独立的SQL,这种写法显然要比叉乘简单许多(是简单,不是短,写的SQL总字数可能更多,但很简单)。然后在报表中先把基准的数据表列出来(这个表中是最左边的一边,即人员姓名),其它片通过条件运算向基准片对齐,如第二片的表达式含义即是找出姓名等于第一列的人的当月收入填入格中,第三片的表达式含义是找出姓名等于第一列的人的当月考勤信息填入,…。这样相当于每一片都和基准片进行了一次叉乘运算,运算复杂度为N1*N2+N1*N3+…+N1*Nk,简单地写就是k-1倍的O(N2),还是O(N2)。这是个平方级的运算,整体复杂度随着分片数增加呈线性增长而非指数增长!效率可以极大地提高。
采用传统工具时,如果运气好了,在数据库中建了合适的索引,数据库有时也会自动把前述的O(Nk)变成上述这种O(N2),但这是没准的事情,而且也不可能在数据库中把所有表都建上索引。
造成传统工具这种问题的设计根源在于其来自国外的数据处理模型。国外应用一般都要求业务系统中事先为查询分析建好模型,然后只需要基于这些模型提供简单的报表,这样很少会出现分片报表,即使有也就是两三片而已。然而国内应用中报表远比国外复杂,仅仅依靠事先建好的数据模型远远不够用,因此常常出现分片数很多的报表。
我们在实际工作中确实碰到过这种现象,5个分片,对应的每个数据表约1万条记录,用brio在oracle数据库中运算了约20分钟,而用博计报表是6秒!