一、出现了什么问题
原先的刷新逻辑是:当有指标更新通知到达时,就刷新该指标节点所在树的所有节点
,客户的生产环境的更新频率好象是200指标每秒,指标节点数应该会有几千个,一打开界面就直接卡死。
现在优化需求主要为两个:
1、性能需求:解决卡死问题。
2、功能需求:父节点要根据所有子节点的最高级别改变颜色,例如子节点中有严重级别,那么它的所有父辈节点都需要变成对应的红色。
二、我的思路
卡死的根本原因是频繁刷新界面,造成它的原因,一个是服务端更新通知太频繁,一个是客户端更新粒度太大,所以处理这个问题,我从两个方向入手:
1、降低通知频率。
2、减少刷新节点个数。
第1个问题很好解决,把原先「单个节点更新就立刻发送通知」的模式,加一个缓存,改为「定时发送时间段内产生的通知」的模式,每秒更新频率立马就下来了。
第2个问题就复杂一点了,我一共优化了两次。
第一次优化
系统预设级别一共有8种,从正常0到严重7,汇总期间涉及大量的并集运算,我很自然的想到使用二进制位表示子节点的某级别是否存在,如「10000001」表示子节点级别有严重和正常,这样可以利用或运算
实现多个节点的级别汇总,遍历一遍就可以得到子节点的最高级别了。
原来的刷新逻辑造成很多不必要的刷新,例如一个父节点下面已经有一个严重的子节点了,那么它的其余子节点再怎么更新,这个父节点都是严重级别,所以我的优化思路是:
1、自底向上更新。
2、更新期间判断节点级别是否发生改变。
所以,刷新的流程大概是这样:
1、节点A级别更新。
2、找到A的父节点FA,汇总FA的所有子节点的级别,并求出最高级别NewMaxLevel。
3、NewMaxLevel与FA的级别比较,如果一样,更新停止,如果不一样,就令A=FA,回到步骤1。
多了一个判断过程,使得可以减少很多的不必要的刷新。
第二次优化
完成第一次优化后,我发现方案还存在优化空间:上述流程的热点代码集中在步骤2(因为涉及树的遍历),如果某FA下有几百几千个指标,那么有可能出现汇总了半天结果发现最高级别没有改变的糟糕情形。
其实父节点根本不需要知道究竟哪个子节点是正常,哪个子节点是严重,它只需要知道所有的子节点的最高级,其实也就是每个级别的节点究竟有多少个。转变了问题方向,那么解决就容易了。我在节点里面加了2个数据,它们的功能分别是
1、记录每个级别的子节点的计数。
2、保存上一次的级别状态,用于更新后减少父节点的计数。
这样,当子节点更新时,只会修改父节点中的计数,而不是让父节点又统计一遍。此时的刷新流程修改成这样:
1、节点A级别更新,记录下OldLevel和NewLevel。
2、找到A的父节点FA,OldLevel对应的计数-1,NewLevel对应的+1,求FA的最高级别NewMaxLevel。
3、NewMaxLevel与FA的OldMaxLevel比较,如果一样,更新停止,如果不一样,就令A=FA,回到步骤1。
后记
至此,是我对这个项目的优化方案。原先的刷新算法复杂度应该是O(n)(n是子节点个数),优化后的是O(1),不涉及遍历,只与整个树的高度有关。