使用xml描述的数据周期统计框架
2014-03-18 22:54
309 查看
项目中经常会用到对一些数据作周期统计,从而生成报表,通常原始的数据是存放在各种数据库表里的,
需要根据特定的规则进行数据统计后输出,通过写数据库的存储过程是一种方式,但存储过程一般难以跨数据库统计,而且限制于特定的数据库脚本语言,
支持不同类型数据库就需要重写脚本,不易维护。所以开发一套通用的数据统计框架以及统计规则描述模板,可以用更简洁的方式来描述出统计的规则,
适用于对不同数据库数据作周期统计,是有其应用价值的。下面介绍下一种我自己定义和实现的统计模板,实现比较粗糙,比如没有很好的语法检查支持等,
但在实际使用过程中还是收到了很好的效果。
首先给大家看一个统计规则模板的例子:
上面的xml定义了数据的来源,处理,和结果:
首先source的sql字段定义了数据的来源,即通过sql语句查询出本次要统计的数据,
其中begin_time和end_time参数是由周期统计框架传人的本次要统计的数据时间区间,由统计框架来确保数据不会被重复统计;
field标签同时定义了统计结果的字段和字段的计算公式即统计规则,可以通过source.$F{field_name} 的格式直接引用sql语句里的原始数据字段的值,
也可以通过$F{field_name}的格式引用其它结果字段的值,另外支持算术表达式的求值,字符串连接,求子串等运算,或者sum,avg,max,min等分组内的求和,求平均,最大最小值的计算;
group标签定义了对那几个字段进行分组,可以对原始sql语句里的字段分组,也可以对结果字段再作分组,且在分组内对结果字段公式里存在的分组聚合函数如sum,avg等作运算;
dst_table_name参数定义了目的表的名称,这样每次统计结束后,统计框架会将统计结果按模板里的字段建立结果数据库表并将统计的结果入库;
下面来看更多的例子:
上面模板中有一个innerfield字段,这个字段是一个临时字段,其结果并不会写入到结果数据,但会作为中间的结果计算出来,类似于我们写函数时定义了一个临时的变量;
上面的innerfield字段的表达式里使用了一个split方法,这个是模板内置的一个方法,它会将第一个参数即原始字段data_list按第二个参数即空格分隔成一个个的子串;
比如上面的data_list记录的是通过telnet执行命令cat /proc/meminfo取到的linux服务器的内存使用情况,内容如前一行注释所示,分隔出来的子串可以通过get方法来通过下标取得;
因此计算memory_usage即内存使用率的字段时,我们通过将total - free - buffers - cached / total 来计算实际的内存使用情况
<?xml version="1.0" encoding="utf-8"?>
<report_def>
<dataset name="swrunmem_usage_collect_report_tmp">
<source>
<sql>
<![CDATA[
SELECT dev_id, task_id, row_index, data_list, collect_time FROM tbl_origin_perf_data
WHERE (task_id between 11 and 12)
AND collect_time >= $P{begin_time_offset} AND collect_time < $P{end_time} order by collect_time;
]]>
</sql>
</source>
<!--task11: hrSWRunName,hrSWRunStatus,hrSWRunPerfCPU,hrSWRunPerfMem,totalcputime -->
<!--task12: User,CPUusage,Memusage,UserNum,cpunum -->
<innerfield name="data" class="long"><![CDATA[split((source.$F{data_list}+",1"), ",")]]></innerfield>
<innerfield name="task_id" class="long"><![CDATA[source.$F{task_id}]]></innerfield>
<field name="dev_id" class="long"><![CDATA[source.$F{dev_id}]]></field>
<field name="swrun_id" class="int"><![CDATA[source.$F{row_index}]]></field>
<field name="procname" class="string"><![CDATA[get($F{data},0)]]></field>
<field name="ctime" class="timestamp"><![CDATA[source.$F{collect_time}]]></field>
<field name="memusage" class="string">
<case>
<pred>source.$F{task_id} == 11</pred>
<![CDATA[expr(get($F{data},3)/1024)]]>
</case>
<otherwise>
<![CDATA[get($F{data},2)]]>
</otherwise>
</field>
<field name="cpuusage" class="string">
<case>
<pred>source.$F{task_id} == 11</pred>
<case>
<pred>get($F{data},2) == 0</pred>
0
</case>
<otherwise>
<![CDATA[expr(delta(get($F{data},2))*100/delta(get($F{data},4)))]]>
</otherwise>
</case>
<otherwise>
<![CDATA[expr(get($F{data},1)/get($F{data},4))]]>
&l
4000
t;/otherwise>
</field>
<field name="procnum" class="int">
<case>
<pred>source.$F{task_id} == 11</pred>
<![CDATA[get($F{data},5)]]>
</case>
<otherwise>
<![CDATA[get($F{data},3)]]>
</otherwise>
</field>
<group>$F{dev_id}</group>
<group>$F{swrun_id}</group>
<group>$F{task_id}</group>
<!-- discard invalid data -->
<filter><![CDATA[($F{ctime} >= $P{begin_time}) || ($F{cpuusage} != 'INF')]]></filter>
<!-- collect time period -->
<param name="begin_time" class="timestamp"/>
<param name="end_time" class="timestamp"/>
<!-- select last old data to calc first record -->
<param name="begin_time_offset" class="string">minioffset($P{begin_time},"-30")</param>
<!-- save each line data when group -->
<param name="show_group_detail" class="string">true</param>
<!-- destination table name -->
<param name="dst_table_name" class="string">'tbl_collect_process_runstatus_tmp'</param>
</dataset>
上面的模板中,同时对保存的两种格式的进程性能数据信息进行了统计,两种数据类型的格式如注释所示,分别是由任务11和任务12采集过来的数据,对应到snmp采集和telnet采集两种方式。
两种类型数据的统计方法我们使用case标签来作区分,对任务11采集的进程数据按snmp格式进行解析,对任务12采集到的进程数据按telnet格式进行解析,如果有更多的情况可以继续使用多个case标签作区分,每个case标签通过pred标签来定义它自己的条件,pred里可以是任意的布尔表达式。
最后统计的结果可以通过由filter标签指定的过滤条件进行过滤,这里将丢弃掉时间范围错误或者除法出现溢出的无效行数据
另外模板内定义的参数也可以引用其它参数并进行计算,比如begin_time_offset参数的值是将begin_time的值往前减掉30分钟,使用到了内置时间函数minioffset;
delta支持对同一分组内前后两行数据进行差值运算,例子中,对于进程的cpu使用率的计算,必须将前后两个采集数据相减后才能得到正确的结果。
需要根据特定的规则进行数据统计后输出,通过写数据库的存储过程是一种方式,但存储过程一般难以跨数据库统计,而且限制于特定的数据库脚本语言,
支持不同类型数据库就需要重写脚本,不易维护。所以开发一套通用的数据统计框架以及统计规则描述模板,可以用更简洁的方式来描述出统计的规则,
适用于对不同数据库数据作周期统计,是有其应用价值的。下面介绍下一种我自己定义和实现的统计模板,实现比较粗糙,比如没有很好的语法检查支持等,
但在实际使用过程中还是收到了很好的效果。
首先给大家看一个统计规则模板的例子:
<dataset name="swrunmem_usage_collect_report"> <source> <sql> <![CDATA[ SELECT dev_id, swrun_id, procname, memusage, cpuusage, procnum, ctime FROM tbl_collect_process_runstatus_tmp WHERE ctime >= $P{begin_time} AND ctime < $P{end_time} order by ctime; ]]> </sql> </source> <field name="dev_id" class="long"><![CDATA[source.$F{dev_id}]]></field> <field name="procname" class="string"><![CDATA[source.$F{procname}]]></field> <field name="ctime" class="timestamp"><![CDATA[source.$F{ctime}]]></field> <field name="memusage" class="string"><![CDATA[expr(1.0*sum(source.$F{memusage}))]]></field> <field name="cpuusage" class="string"><![CDATA[expr(1.0*sum(source.$F{cpuusage}))]]></field> <field name="procnum" class="int"><![CDATA[sum(source.$F{procnum})]]></field> <group>$F{dev_id}</group> <group>$F{procname}</group> <group>$F{ctime}</group> <!-- collect time period --> <param name="begin_time" class="timestamp"/> <param name="end_time" class="timestamp"/> <!-- destination table name --> <param name="dst_table_name" class="string">'tbl_collect_process_runstatus'</param> </dataset>
上面的xml定义了数据的来源,处理,和结果:
首先source的sql字段定义了数据的来源,即通过sql语句查询出本次要统计的数据,
其中begin_time和end_time参数是由周期统计框架传人的本次要统计的数据时间区间,由统计框架来确保数据不会被重复统计;
field标签同时定义了统计结果的字段和字段的计算公式即统计规则,可以通过source.$F{field_name} 的格式直接引用sql语句里的原始数据字段的值,
也可以通过$F{field_name}的格式引用其它结果字段的值,另外支持算术表达式的求值,字符串连接,求子串等运算,或者sum,avg,max,min等分组内的求和,求平均,最大最小值的计算;
group标签定义了对那几个字段进行分组,可以对原始sql语句里的字段分组,也可以对结果字段再作分组,且在分组内对结果字段公式里存在的分组聚合函数如sum,avg等作运算;
dst_table_name参数定义了目的表的名称,这样每次统计结束后,统计框架会将统计结果按模板里的字段建立结果数据库表并将统计的结果入库;
下面来看更多的例子:
<?xml version="1.0" encoding="utf-8"?> <report_def> <dataset name="mem_usage_collect_telnet_report"> <source> <sql> <![CDATA[ SELECT dev_id, task_id, row_index, data_list, collect_time FROM tbl_origin_perf_data WHERE task_id=7 AND collect_time >= $P{begin_time} AND collect_time < $P{end_time} order by collect_time; ]]> </sql> </source> <field name="dev_id" class="long"><![CDATA[source.$F{dev_id}]]></field> <field name="memory_id" class="int"><![CDATA[source.$F{row_index}]]></field> <field name="ctime" class="timestamp"><![CDATA[source.$F{collect_time}]]></field> <!-- MemTotal: 255764 kB MemFree: 118132 kB Buffers: 11472 kB Cached: 87772 kB --> <innerfield name="data" class="long"><![CDATA[split(source.$F{data_list}, " ")]]></innerfield> <field name="memory_usage" class="int"> <![CDATA[expr((get($F{data},1)-get($F{data},4)-get($F{data},7)-get($F{data},10))*100/get($F{data},1))]]> </field> <!-- collect time period --> <param name="begin_time" class="timestamp"/> <param name="end_time" class="timestamp"/> <!-- destination table name --> <param name="dst_table_name" class="string">'tbl_collect_memory_usage'</param> </dataset> </report_def>
上面模板中有一个innerfield字段,这个字段是一个临时字段,其结果并不会写入到结果数据,但会作为中间的结果计算出来,类似于我们写函数时定义了一个临时的变量;
上面的innerfield字段的表达式里使用了一个split方法,这个是模板内置的一个方法,它会将第一个参数即原始字段data_list按第二个参数即空格分隔成一个个的子串;
比如上面的data_list记录的是通过telnet执行命令cat /proc/meminfo取到的linux服务器的内存使用情况,内容如前一行注释所示,分隔出来的子串可以通过get方法来通过下标取得;
因此计算memory_usage即内存使用率的字段时,我们通过将total - free - buffers - cached / total 来计算实际的内存使用情况
<?xml version="1.0" encoding="utf-8"?>
<report_def>
<dataset name="swrunmem_usage_collect_report_tmp">
<source>
<sql>
<![CDATA[
SELECT dev_id, task_id, row_index, data_list, collect_time FROM tbl_origin_perf_data
WHERE (task_id between 11 and 12)
AND collect_time >= $P{begin_time_offset} AND collect_time < $P{end_time} order by collect_time;
]]>
</sql>
</source>
<!--task11: hrSWRunName,hrSWRunStatus,hrSWRunPerfCPU,hrSWRunPerfMem,totalcputime -->
<!--task12: User,CPUusage,Memusage,UserNum,cpunum -->
<innerfield name="data" class="long"><![CDATA[split((source.$F{data_list}+",1"), ",")]]></innerfield>
<innerfield name="task_id" class="long"><![CDATA[source.$F{task_id}]]></innerfield>
<field name="dev_id" class="long"><![CDATA[source.$F{dev_id}]]></field>
<field name="swrun_id" class="int"><![CDATA[source.$F{row_index}]]></field>
<field name="procname" class="string"><![CDATA[get($F{data},0)]]></field>
<field name="ctime" class="timestamp"><![CDATA[source.$F{collect_time}]]></field>
<field name="memusage" class="string">
<case>
<pred>source.$F{task_id} == 11</pred>
<![CDATA[expr(get($F{data},3)/1024)]]>
</case>
<otherwise>
<![CDATA[get($F{data},2)]]>
</otherwise>
</field>
<field name="cpuusage" class="string">
<case>
<pred>source.$F{task_id} == 11</pred>
<case>
<pred>get($F{data},2) == 0</pred>
0
</case>
<otherwise>
<![CDATA[expr(delta(get($F{data},2))*100/delta(get($F{data},4)))]]>
</otherwise>
</case>
<otherwise>
<![CDATA[expr(get($F{data},1)/get($F{data},4))]]>
&l
4000
t;/otherwise>
</field>
<field name="procnum" class="int">
<case>
<pred>source.$F{task_id} == 11</pred>
<![CDATA[get($F{data},5)]]>
</case>
<otherwise>
<![CDATA[get($F{data},3)]]>
</otherwise>
</field>
<group>$F{dev_id}</group>
<group>$F{swrun_id}</group>
<group>$F{task_id}</group>
<!-- discard invalid data -->
<filter><![CDATA[($F{ctime} >= $P{begin_time}) || ($F{cpuusage} != 'INF')]]></filter>
<!-- collect time period -->
<param name="begin_time" class="timestamp"/>
<param name="end_time" class="timestamp"/>
<!-- select last old data to calc first record -->
<param name="begin_time_offset" class="string">minioffset($P{begin_time},"-30")</param>
<!-- save each line data when group -->
<param name="show_group_detail" class="string">true</param>
<!-- destination table name -->
<param name="dst_table_name" class="string">'tbl_collect_process_runstatus_tmp'</param>
</dataset>
上面的模板中,同时对保存的两种格式的进程性能数据信息进行了统计,两种数据类型的格式如注释所示,分别是由任务11和任务12采集过来的数据,对应到snmp采集和telnet采集两种方式。
两种类型数据的统计方法我们使用case标签来作区分,对任务11采集的进程数据按snmp格式进行解析,对任务12采集到的进程数据按telnet格式进行解析,如果有更多的情况可以继续使用多个case标签作区分,每个case标签通过pred标签来定义它自己的条件,pred里可以是任意的布尔表达式。
最后统计的结果可以通过由filter标签指定的过滤条件进行过滤,这里将丢弃掉时间范围错误或者除法出现溢出的无效行数据
另外模板内定义的参数也可以引用其它参数并进行计算,比如begin_time_offset参数的值是将begin_time的值往前减掉30分钟,使用到了内置时间函数minioffset;
delta支持对同一分组内前后两行数据进行差值运算,例子中,对于进程的cpu使用率的计算,必须将前后两个采集数据相减后才能得到正确的结果。
相关文章推荐
- 使用 .NET 框架中集成的读取器和写入器很容易操作 XML 数据
- iOS开发使用GDataXML框架解析网络数据
- 使用Enumerable模块实现简单的测试框架并进行数据统计
- 使用第三方框架Xstream轻松解析xml数据
- iOS开发之网络数据解析--GDataXML解析框架的使用
- WebService CXF框架的使用(实现JSON与XML数据传输)
- 真实世界的 XML:使用 .NET 框架中集成的读取器和写入器很容易操作 XML 数据
- 初学_Android4高级编程-7 异步http框架得到有道翻译的XML与json资源并解析出需要的数据&使用DownloadManager
- iOS开发之网络数据解析--GDataXML解析框架的使用
- 使用MapReduce计算框架统计CDN日志IP数、流量等数据
- 您的项目引用了最新实体框架;但是,找不到数据链接所需的与版本兼容的实体框架数据库 EF6使用Mysql的技巧
- 使用javascript解析xxx.xml文档将数据加载到xxx.html文档中的表格元素中
- 使用RecyclerView获取xml数据(csdn的数据)并解析展示(二)
- CYQ.Data 数据框架 使用篇一 入门指南
- 这5种必知的大数据处理框架技术,你的项目到底应该使用其中的哪几种
- android轻量级缓存框架ASimpleCache的使用 (网络请求数据并缓存)
- ActionScript 3.0 Step By Step系列(七):使用XML和XMLList类处理XML数据
- windows8开发-metro应用中使用xml+json作为数据存储方式
- xml数据处理--expat模块使用
- .NET 框架中的 XML:在 .NET 框架中使用 XML 架构执行代码生成