Fork me on GitHub
大数据量下查询显示优化

大数据量下查询显示优化方案小结

最近工作中,遇到了优化大批量数据查询和显示的问题,数据量在10W级别。经过反复设计和讨论,最终得到优化到了较为满意的效果,在此记录小结下,在解决此类问题中的思考。

问题背景说明

通常情况下,用户查询数据量不超过1千条,但有几个大户,通过某种方式,生成了上万级别的数据,前端未针对大数据量的查询和显示进行优化,导致该界面显示卡顿、白屏、点击无响应、显示总量和实际总量不一致等等问题。

总体思路

大数据量查询和显示,可从以下三个方面来优化:

  • 定时分批查询
  • 缓存中心
  • UI优化

以上三层分别对应数据层、中间层和UI层,每层优化方案可单独使用,也可以结合使用。建议结合使用。

定时分批查询

数据后台提供的查询指令是支持分页查询的,与其相关的字段有:

  • 查询方向 qryDir, 定位查询方向,1为往下查,0为往上查
  • 请求行数 qryCount,说明期望查询的个数
  • 定位串 qryPositionStr, 查询定位串,用于定位查询起点

基于上述三个字段,系统可以实现分页查询功能,但具体到UI层,因为无法获知该用户数据总量,也就无法推导总页数信息,UI层无法以分页形式展示。如果后台提供数据总量字段,那就可方便实现上一页、下一页、最后一页等功能。现有后台没提供总量信息,无法在UI层做分页查询,只在数据层做分页查询。

在数据层做分页查询时,对UI层是透明的。也就是说,当数据层尚未查询完毕时,UI层是不会收到响应,还可以继续操作界面。这里可以优化,同产品讨论以下两种处理方式:

  • 方案一:本次查询尚未返回,禁止UI层重复查询
  • 方案二:本次查询尚未返回,支持UI层重复查询

目前的设计采用方案二,因此需要数据层考虑重复请求的场景。除了重复请求之外,数据层还需要考虑后台限制和缓存管理,小结起来有以下三点:

  1. 后台限制
  2. 缓存管理
  3. 重复查询

下面分别阐述数据层针对上述问题的设计方案。

  1. 后台限制

任何后台服务能够提供的功能都是有限制要求的。数据后台对查询指令有单次最大条数和查询频率的限制,具体限制如下:

  • 单次查询最大请求行数建议1000条
  • 查询频率限制在5秒15笔查询,超频查询会报错

为了在不影响后台稳定性的情况下,尽可能快地查询回所有信息。在查询过程中,除了常规的分页查询之外,还额外增加定时查询机制,具体设计为,每连续发起3笔分页查询,等待半秒后,继续查询,循环往复,直到所有查询数据都返回,每次分页查询请求条数设置后台推荐的最大值。上述每3笔休息半秒需要根据后台具体限制来设置,考虑到系统还有其他查询指令在同时进行,设置宽松些比较合适。要明确这一点,在分页查询过程中触发流量超限错误,那么之前已查询的数据都会无效,要慎重设置定时间隔,宁可慢一点,不能出错。

  1. 缓存管理

此处的缓存指的是数据层的缓存,而不是缓存中心的缓存。此处的缓存用于保存分页查询返回的数据,等到所有查询完毕,再汇总往上传递。这里要注意的点,是要区分以下两种情况:

  • 由缓存中心主动发起的分页查询
  • 由数据层发起的分页查询

上述两种情况,单靠分页查询标志无法区分,需要增加额外的标识信息来区分,在缓存中心需要根据此标志进行查询数据的合并和过滤。

  1. 重复查询

当数据层查询尚未完成,又有重复查询的情况,按照场景可分为

  • UI层发起新的全量查询
  • 缓存中心定时发起增量查询

针对UI层发起新的全量查询,作为数据层有两种应对策略:

  • 方案一:本次查询尚未完成,拒绝新的查询
  • 方案二:本次查询尚未完成,允许新的查询,同时清空上次已查询得到的信息

由于数据层是服务提供方,提供给UI层和缓存中心层使用,由于UI层支持重复查询,缓存中心层支持定时查询,所以数据层采用第二种方案。

针对缓存中心定时发起增量查询,当上一次全量或者增量查询尚未返回时,缓存中心要禁止新的增量查询请求。

缓存中心

为了管理缓存以及提供缓存数据接口,设计缓存中心,它介于UI层和数据层之间,UI层通过订阅数据消息,来获得响应数据。查询中心在查询到数据后,通过发布通知的方式,更新界面数据。

在缓存中心中,对外提供两个公共接口:

  • ForceRefreshData: 主动重新获取全量数据
  • GetDataByAccount:按照账号获取全量数据

在缓存中心内部,通过定时器来触发分页查询。当查询完毕时,通过订阅发布机制向有需要的界面推送数据,推送的数据既包括全量数据,也包括增量数据,便于UI层使用。

在缓存中心处理过程中,增量数据的来源有两处:

  • 定时增量请求
  • 两次全量请求

无论是哪种来源,都需要和已有全量查询结果进行对比,得出增量数据。当数据量很大时,从新的全量查询和已有全量查询中对比获得增量数据,时间复杂度很高,比如全量有5W数据,新的全量有6W数据,那么从6W数据中去掉已有的5W数据,得到增量1W数据,双重循环的话,会操作6W*5W次,可通过stl::set结构来加速过滤筛选,其中的key设置为可唯一标识数据的属性组合即可。

缓存中心要考虑增量查询和全量查询冲突的问题,也就是说,要考虑以下表格中的四种场景:

当前的查询场景增量查询全量查询
增量查询 增量-增量 增量-全量
全量查询 全量-增量 全量-增量

在具体实现时,建议缓存中心中的增量查询和全量查询复用同个请求,保存一份全量数据成员即可。

UI优化

UI优化可分为交互优化和刷新优化两个方面,以下分别来描述。

交互优化

为减少可能的重复全量查询,可在UI层提示用户,该界面会主动接受数据推送(实际实现,可能是定时查询、也可以是接收后台主推),无需频繁查询,但不禁止用户主动刷新。

另外的话,在用户主动刷新后,界面增加查询定时器,告知用户已查询耗时时间,已查询到的数据量,该数据量可按照每次查询1000条,根据底层逻辑来大概预估已查询到的数据条数,等到数据层全部查询完毕后,显示最终准确数值。

刷新优化

本次优化是在Windows系统上,使用CListCtrl控件的Report模式来显示数据。刷新优化可以从以下三点出发:

  • 虚表模式赋值
  • 界面数据增量刷新
  • 界面显示局部刷新

虚表模式赋值,是MFC中提供的加快列表控件显示速度的方案,具体参见 CListCtrl虚拟列表技术,此处不再叙述。

界面增量刷新,是指在UI层维护当前显示内容缓存,当有数据更新时,通过比较更新数据和当前缓存,决定是否更新列表。基于上述缓存中心,可通过增量更新来优化判断流程。

界面局部刷新,是指只针对当前界面显示部分进行刷新,未显示部分不进行刷新。界面局部刷新涉及到的函数有以下三个:

具体使用很简单,在重绘时,只需要以下两句话搞定:


int nFirst = GetTopIndex();
RedrawItems(nFirst, nFirst + GetCountPerPage())

以上方案实现了界面显示局部刷新,配合虚表模式赋值界面数据增量刷新,可极大提高在大数据量下的CListCtrl刷新显示效果,亲测有效,值得使用。

全文总结

本文总结大数据量下的查询和显示优化方案,主要从数据层、中间层和UI层进行优化,方案设计思路如下:

  • 数据层的定时分批查询
    • 根据后台限制优化定时查询间隔
    • 缓存管理
    • 考虑重复查询场景
  • 中间层的缓存中心
    • 支持定时增量查询
    • 推送数据既包括全量也包括增量
  • UI层的交互优化和刷新优化
    • 交互优化
    • 虚表模式赋值
    • 界面数据增量刷新
    • 界面显示局部刷新

以上是本人在大数据量下查询和显示优化方案的思考,文章可能会有纰漏和错误,欢迎大家在留言中指出来,大家一起讨论学习,共同进步!

posted on 2020-04-03 22:00  HackerVirus  阅读(873)  评论(0编辑  收藏  举报