数据量太大的情况下,现场计算一定会超时;
统计功能步骤
1.查询并计算数据(简单)
2.将数据存到中间表(简单)
3.修改统计数据的时候更新中间表(复杂)
4.实时刷新展示数据(复杂又麻烦)
中间表使用
以告警业务的统计举例,设计一套快速稳定的功能
查询时(查询包含了初始化,增量新增,更新)
1.查询:当天的直接计算(当天数据变化快),之前的数据从中间表获取;
2.初始化:中间表取出数据后,按日期检测是否有数据为空,如果缺失(比如统计3,4,5月的数据,其中4月数据为null,说明4月数据没生成过),去业务表中获取并计算;然后保存到中间表;第一次使用就是初始化的效果。
3.增量新增:一般情况下只有昨天的数据需要新增。
4.更新:如果老数据有更新的可能,需要再次去业务表里查询并计算;(比如未处理状态的告警,需要去查询是否已经处理完了)
这里有两个处理方式。
4.1.业务表修改时加上需要修改标识,或直接修改中间表,
4.2.定期或查询时去复查可能更新的数据,最好定个范围;比如一个月内的未处理告警,每次点击统计查询的时候会去业务表复查。
我是两个方式都加上了,团队开发的时候4.1可能无法做到全部修改都覆盖到,而且测试环境经常会有数据库直接修改数据。
统计颗粒度权衡
这么麻烦的设计,如果只对应一个功能就浪费了;
怎么样让统计表可以通用的同时还能保证效率?
首先是统计的颗粒度,按告警类型,告警状态,告警地区,告警日期分别统计;
假如要展示温度告警的统计,那把高温告警和低温告警相加后展示;但是如果太细了,计算就会很频繁,那就失去了统计表的效率,如果太粗了,复用性就很差;比如只按市区统计,客户想看区县的统计数据就看不到了。
可以用空间换时间,例如同时保存县区和市区的统计数据,同时保存月和日的统计数据;
保证每次计算量不要太大,比如查询3月1日到5月4日的统计数据,只需要计算3月+4月+5月1号+5月2号+5月3号+5月4号+5月5号,统计一年内的也不会超过80次的循环相加。
数据同步注意事项
统计难度最大的就是数据同步,如果处理不好,很容易就会出现数据对不上的问题。
继续用告警功能举例,首先要确定告警数据在哪些功能里会被修改,如果项目代码写的规范,只需要在告警的修改方法统一处理即可。
具体步骤是
1.这条告警状态被修改了,为处理->以处理;插入两条redis数据key1=修改前状态+告警类型+告警地区+告警日期;key2=修改后状态+告警类型+告警地区+告警日期;值随意,主要是key。
2.统计表查询时如果查询到redis中key对应的数据,就要重新计算,然后替换统计表里的数据。(一定要记得替换,这里容易出bug)
由于我计算统计数据的最小查询量是一天(把一整天的告警数据查询出来,在按照条件来分类统计,比按条件多次访问数据库更有效率),所以我的key只有日期。key1=告警日期;key2=告警月;(同时保存了月和日的统计所以要一起更新)
如果发生了正好每天的数据都修改了,然后再查询一次,那就和初始化一样,非常慢,还可能超时。
优化
1.使用消息队列,告警状态被修改了,保存redis数据的同时插入一条延迟消息,5分钟后更新相关统计(为什么要延迟,如果修改一条就要重新统计一次,那太浪费资源了,延迟5分钟可以把5分钟内的所有修改一起更新了);
2.redis保存的值不再是随意写的,从0开始累加,设定一个阈值,比如10,超过10就更新相关统计。
这些操作都是为了防止统计更新集中在查询的时候,这两个设计很像mysql的刷盘机制,所以好的设计都是可以互相借鉴的,经典永流传;但是实际运用上查询比修改更频繁;上述优化是可选的。
功能设计的越复杂,用起来更顺畅,但是维护起来更难;所以量力而行。
最酷炫的实时刷新怎么做
打开统计页面,页面上的数字会根据告警的新增修改,实时变化。
这个要借助websocket,和定时任务来实现。
未完待续...
网友评论