您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

微信海量数据查询如何从1000ms降到100ms?

时间:2023-06-01 16:25:32  来源:今日头条  作者:闪念基因

导读

微信的多维指标监控平台,具备自定义维度、指标的监控能力,主要服务于用户自定义监控。作为框架级监控的补充,它承载着聚合前 45亿/min、4万亿/天的数据量。当前,针对数据层的查询请求也达到了峰值 40万/min,3亿/天。较大的查询请求使得数据查询遇到了性能瓶颈:查询平均耗时 > 1000ms,失败率居高不下。针对这些问题,微信团队对数据层查询接口进行了针对性的优化来满足上述场景,将平均查询速度从1000ms+优化到了100ms级别。本文为各位分享优化过程,希望对你有用!

目录

1 背景介绍

2 优化分析

2.1 用户查询行为分析

2.2 数据层架构

2.3 为什么查询会慢

3 优化方案设计

3.1 拆分子查询请求

3.2 拆分子查询请求+redis Cache

3.3 更进一步-子维度表

4 优化成果

4.1 缓存命中率>85%

4.2 查询耗时优化至 100ms

5 结语

 

01

 

背景介绍

 

微信多维指标监控平台(以下简称多维监控),是具备灵活的数据上报方式、提供维度交叉分析的实时监控平台。

 

在这里,最核心的概念是“协议”、“维度”与“指标”。例如,如果想要对某个【省份】、【城市】、【运营商】的接口【错误码】进行监控,监控目标是统计接口的【平均耗时】和【上报量】。在这里,省份、城市、运营商、错误码,这些描述监控目标属性的可枚举字段称之为“维度”,而【上报量】、【平均耗时】等依赖“聚合计算”结果的数据值,称之为“指标”。而承载这些指标和维度的数据表,叫做“协议”。

 

多维监控对外提供 2 种 API:

 

  • 维度枚举查询:用于查询某一段时间内,一个或多个维度的排列组合以及其对应的指标值。它反映的是各维度分布“总量”的概念,可以“聚合”,也可以“展开”,或者固定维度对其它维度进行“下钻”。数据可以直接生成柱状图、饼图等。
  • 时间序列查询:用于查询某些维度条件在某个时间范围的指标值序列。可以展示为一个时序曲线图,横坐标为时间,纵坐标为指标值。
  •  

然而,不管是用户还是团队自己使用多维监控平台的时候,都能感受到明显的卡顿。主要表现在看监控图像或者是查看监控曲线,都会经过长时间的数据加载。

 

团队意识到,这是数据量上升必然带来的瓶颈。目前,多维监控平台已经接入了数千张协议表,每张表的特点都不同。维度组合、指标量、上报量也不同。针对大量数据的实时聚合以及 OLAP 分析,数据层的性能瓶颈越发明显,严重影响了用户体验。于是这让团队人员不由得开始思考:难道要一直放任它慢下去吗?答案当然是否定的。因此,微信团队针对数据层的查询进行了优化。

 

02

 

优化分析

 

2.1 用户查询行为分析

 

要优化,首先需要了解用户的查询习惯,这里的用户包含了页面用户和异常检测服务。于是微信团队尽可能多地上报用户使用多维监控平台的习惯,包括但不限于:常用的查询类型、每个协议表的查询维度和查询指标、查询量、失败量、耗时数据等。

 

在分析了用户的查询习惯后,有了以下发现:

 

  • 【时间序列】查询占比 99% 以上

出现如此悬殊的比例可能是因为:调用一次维度枚举,即可获取所关心的各个维度。但是针对每个维度组合值,无论是页面还是异常检测都会在查询维度对应的多条时间序列曲线中,从而出现「时间序列查询」比例远远高于「维度枚举查询」。

 

  • 针对1天前的查询占比约 90%

出现这个现象可能是因为每个页面数据都会带上几天前的数据对比来展示。异常检测模块每次会对比大约 7 天数据的曲线,造成了对大量的非实时数据进行查询。

 

2.2 数据层架构

 

分析完用户习惯,再看下目前的数据层架构。多维监控底层的数据存储/查询引擎选择了 Apache-Druid 作为数据聚合、存储的引擎,Druid 是一个非常优秀的分布式 OLAP 数据存储引擎,它的特点主要在于出色的预聚合能力和高效的并发查询能力,它的大致架构如图:

 

节点

解析

Mater节点

Overlord:实时数据摄入消费控制器

 

Coordinator:协调集群上数据分片的发布和负载均衡

实时节点

MiddleManager:实时数据写入中间管理者,创建 Peon 节点进行数据消费任务并管理其生命周期

 

Peon:消费实时数据,打包并发布实时数据分片

存储节点

Historical:存储数据分片
DeepStorage:分片中转存储,不对外查询
MetaDataStorage:元信息,如表结构
Zookeeper:存储实时任务和状态信息

 

2.3 为什么查询会慢

 

查询慢的核心原因,经微信团队分析如下:

 

  • 协议数据分片存储的数据片段为 2-4h 的数据,每个 Peon 节点消费回来的数据会存储在一个独立分片。
  • 假设异常检测获取 7 * 24h 的数据,协议一共有 3 个 Peon 节点负责消费,数据分片量级为 12*3*7 = 252,意味着将会产生 252次 数据分片 I/O。
  • 在时间跨度较大时、MiddleManager、Historical 处理查询容易超时,Broker 内存消耗较高。
  • 部分协议维度字段非常复杂,维度排列组合极大 (>100w),在处理此类协议的查询时,性能就会很差。

 

03

 

优化方案设计

 

根据上面的分析,团队确定了初步的优化方向:

 

  • 减少单 Broker 的大跨度时间查询。
  • 减少 Druid 的 Segments I/O 次数。
  • 减少 Segments 的大小。

 

3.1 拆分子查询请求

 

在这个方案中,每个查询都会被拆解为更细粒度的“子查询”请求。例如连续查询 7 天的时间序列,会被自动拆解为 7 个 1天的时间序列查询,分发到多个 Broker,此时可以利用多个 Broker 来进行并发查询,减少单个 Broker 的查询负载,提升整体性能。

 

但是这个方案并没有解决 Segments I/O 过多的问题,所以需要在这里引入一层缓存。

 

3.2 拆分子查询请求+Redis Cache

 

这个方案相较于 v1,增加了为每个子查询请求维护了一个结果缓存,存储在 Redis 中:

 

假设获取 7*24h 的数据,Peon 节点个数为 3,如果命中缓存,只会产生 3 次 Druid 的 Segments I/O (最近的 30min)数据,相较几百次 Segments I/O 会大幅减少。

接下来看下具体方法:

 

3.2.1 时间序列子查询设计

 

针对时间序列的子查询,子查询按照「天」来分解,整个子查询的缓存也是按照天来聚合的。以一个查询为例:

 

{
    "biz_id": 1, // 查询协议表ID:1
    "formula": "avg_cost_time", // 查询公式:求平均
    "keys": [
        // 查询条件:维度xxx_id=3
        {"field": "xxx_id", "relation": "eq", "value": "3"}
    ],
    "start_time": "2020-04-15 13:23", // 查询起始时间
    "end_time": "2020-04-17 12:00" // 查询结束时间
}

其中 biz_id、 formula,、keys 了每个查询的基本条件。但每个查询各不相同,不是这次讨论的重点。

 

本次优化的重点是基于查询时间范围的子查询分解,而对于时间序列子查询分解的方案则是按照「天」来分解,每个查询都会得到当天的全部数据,由业务逻辑层来进行合并。

 

举个例子,04-15 13:23 ~ 04-17 08:20 的查询,会被分解为 04-15、04-16、04-17 三个子查询,每个查询都会得到当天的全部数据,在业务逻辑层找到基于用户查询时间的偏移量,处理结果并返回给用户。

 

每个子查询都会先尝试获取缓存中的数据,此时有两种结果:

结果

解析

缓存未命中

如果子查询结果在缓存中不存在,即 cache miss。只需要将调用 DruidBorker 获取数据,异步写入缓存中,同时该子查询缓存的修改的时间即可。

缓存命中

 

在谈论命中之前,首先引入一个概念「阈值时间(threshold_time)。它表示缓存更新前的一段时间(一般为10min)。我们默认缓存中的数据是不被信任的,因为可能因为数据积压等情况导致一部分数据延迟入库。

 

如果子查询命中了缓存,则存在两种情况:「缓存部分命中」和「缓存完全命中」。其中部分命中如下图所示。

 

  • 缓存部分被命中:

end_time > cache_update_time - threshold_time:这种情况说明了「缓存部分被命中」,从 cache_update_time-thresold_time 到 end_time 这段时间都不可信,这段不可信的数据需要从 DruidBroker 中查询,并且在获取到数据后异步回写缓存,更新 update 时间。

 

 

 

  • 缓存完全命中:
  • 而缓存完全命中则是种理想形式:end_time > cache_update_time - threshold_time。这种情况说明了缓存被完全命中,缓存中的数据都可以被相信,这种情况下直接拿出来就可以了。
  •  

 

 

经过上述分析不难看出:对于距离现在超过一天的查询,只需要查询一次,之后就无需访问 DruidBroker 了,可以直接从缓存中获取。

 

而对于一些实时热数据,其实只是查询了
cache_update_time-threshold_time 到 end_time 这一小段的时间。在实际应用里,这段查询时间的跨度基本上在 20min 内,而 15min 内的数据由 Druid 实时节点提供。

 

3.2.2 维度组合子查询设计

 

维度枚举查询和时间序列查询不一样的是:每一分钟,每个维度的量都不一样。而维度枚举拿到的是各个维度组合在任意时间的总量,因此基于上述时间序列的缓存方法无法使用。在这里,核心思路依然是打散查询和缓存。对此,微信团队使用了如下方案:

 

缓存的设计采用了多级冗余模式,即每天的数据会根据不同时间粒度:天级、4小时级、1 小时级存多份,从而适应各种粒度的查询,也同时尽量减少和 Redis 的 IO 次数。

 

每个查询都会被分解为 N 个子查询,跨度不同时间,这个过程的粗略示意图如下:

 

举个例子:例如 04-15 13:23 ~ 04-17 08:20 的查询,会被分解为以下 10 个子查询:

04-15 13:23 ~ 04-15 14:00

04-15 14:00 ~ 04-15 15:00

04-15 15:00 ~ 04-15 16:00

04-15 16:00 ~ 04-15 20:00

04-15 20:00 ~ 04-16 00:00

04-16 00:00 ~ 04-17 00:00

04-17 00:00 ~ 04-17 04:00

04-17 00:00 ~ 04-17 04:00

04-17 04:00 ~ 04-17 08:00

04-17 08:00 ~ 04-17 08:20

 

这里可以发现,查询 1 和查询 10,绝对不可能出现在缓存中。因此这两个查询一定会被转发到 Druid 去进行。2~9 查询,则是先尝试访问缓存。如果缓存中不存在,才会访问 DruidBroker,在完成一次访问后将数据异步回写到 Redis 中。

 

维度枚举查询和时间序列一样,同时也用了 update_time 作为数据可信度的保障。因为最细粒度为小时,在理想状况下一个时间跨越很长的请求,实际上访问 Druid 的最多只有跨越 2h 内的两个首尾部查询而已。

 

3.3 更进一步-子维度表

 

通过子查询缓存方案,我们已经限制了 I/O 次数,并且保障 90% 的请求都来自于缓存。但是维度组合复杂的协议,即 Segments 过大的协议,仍然会消耗大量时间用于检索数据。

 

所以核心问题在于:能否进一步降低 Segments 大小?

 

维度爆炸问题在业界都没有很好的解决方案,大家要做的也只能是尽可能规避它,因此这里,团队在查询层实现了子维度表的拆分以尽可能解决这个问题,用空间换时间,具体做法为:

● 对于维度复杂的协议,抽离命中率高的低基数维度,建立子维度表,实时消费并入库数据。

● 查询层支持按照用户请求中的查询维度,匹配最小的子维度表。

 

 

04

 

优化成果

 

4.1 缓存命中率>85%

 

在做完所有改造后,最重要的一点便是缓存命中率。因为大部分的请求来自于1天前的历史数据,这为缓存命中率提供了保障:

 

  • 子查询缓存完全命中率(无需查询Druid):86%
  • 子查询缓存部分命中率(秩序查询增量数据):98.8%

 

最明显的效果就是,查询访问 Druid 的请求,下降到了原来的 10% 左右。

 

4.2 查询耗时优化至 100ms

 

在整体优化过后,查询性能指标有了很大的提升:

平均耗时 1000+ms -> 140ms;P95:5000+ms -> 220ms。

 


 

05

 

结语

 

微信多维指标监控平台 ,是微信监控平台的重要组成部分。在分析了用户数据查询行为之后,我们找到了数据查询慢的主要原因,通过减少单 Broker 的大跨度时间查询、减少 Druid 的 Segments I/O 次数、减少 Segments 的大小。我们实现了缓存命中率>85%、查询耗时优化至 100ms。当然,系统功能目前也或多或少尚有不足,在未来团队会继续探索前行,力求使其覆盖更多的场景,提供更好的服务。

 

以上是本次分享全部内容,欢迎大家在评论区分享交流。如果觉得内容有用,欢迎转发~

 

作者:仇弈彬

来源:微信公众号:腾讯云开发者

出处
:https://mp.weixin.qq.com/s/_hqYCY-ySKxSkYNC5WXH2g



Tags:数据查询   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
SQL Server 百万数据查询优化技巧三十则
互联网时代的进程越走越深,使用MySQL的人也越来越多,关于MySQL的数据库优化指南很多,而关于SQL SERVER的T-SQL优化指南看上去比较少,近期有学习SQLSERVER的同学问到SQL SERVER数...【详细内容】
2023-11-28  Search: 数据查询  点击:(339)  评论:(0)  加入收藏
微信海量数据查询如何从1000ms降到100ms?
导读微信的多维指标监控平台,具备自定义维度、指标的监控能力,主要服务于用户自定义监控。作为框架级监控的补充,它承载着聚合前 45亿/min、4万亿/天的数据量。当前,针对数据层...【详细内容】
2023-06-01  Search: 数据查询  点击:(320)  评论:(0)  加入收藏
Oracle数据查询和操作策略:优化数据管理的秘籍
当涉及到 Oracle 数据库的数据查询和操作时,有几个基本的概念和语句需要了解。下面将详细解释每个方面,并提供示例代码。 使用 SELECT 语句进行基本查询: SELECT 语句是查询数...【详细内容】
2023-05-20  Search: 数据查询  点击:(60)  评论:(0)  加入收藏
比Hive快十倍的大数据查询利器,你知道吗?
目前最流行的大数据查询引擎非hive莫属,它是基于MR的类SQL查询工具,会把输入的查询SQL解释为MapReduce,能极大的降低使用大数据查询的门槛, 让一般的业务人员也可以直接对大数据...【详细内容】
2023-03-07  Search: 数据查询  点击:(204)  评论:(0)  加入收藏
MySQL千万级数据查询的优化技巧及思路
随着数据量的不断增长,MySQL千万级数据查询的优化问题也日益引人注目。在这篇文章中,我们将深入探讨MySQL千万级数据查询优化的方法和技巧,以帮助开发者更好地优化MySQL性能。...【详细内容】
2023-02-25  Search: 数据查询  点击:(145)  评论:(0)  加入收藏
面对千万级数据查询,CK、ES、RediSearch谁才是王炸?
前言在开发中遇到一个业务诉求,需要在千万量级的底池数据中筛选出不超过 10W 的数据,并根据配置的权重规则进行排序、打散(如同一个类目下的商品数据不能连续出现 3 次)。下面对...【详细内容】
2022-09-08  Search: 数据查询  点击:(391)  评论:(0)  加入收藏
3000字详解Pandas数据查询,建议收藏
作者:俊欣来源:关于数据分析与可视化大家好,又是新的一周,也是2021年的最后一周,今天小编来和大家说一说怎么从DataFrame数据集中筛选符合指定条件的数据,希望会对读者朋友有所帮...【详细内容】
2021-12-30  Search: 数据查询  点击:(245)  评论:(0)  加入收藏
分布式SQL大数据查询引擎的发展
基于SQL的查询引擎简介,包括指向数据仓库和数据湖的链接> Photo by NASA on Unsplash介绍从高层的角度来看,许多数据和分析解决方案已经以相同的方式构建了许多年。 简而言之,...【详细内容】
2020-11-26  Search: 数据查询  点击:(403)  评论:(0)  加入收藏
万亿条数据查询如何做到毫秒级响应?
知乎,在古典中文中意为“你知道吗?”,它是中国的 Quora,一个问答网站,其中各种问题由用户社区创建,回答,编辑和组织。作为中国最大的知识共享平台,我们目前拥有 2.2 亿注册用户,3000...【详细内容】
2020-08-03  Search: 数据查询  点击:(328)  评论:(0)  加入收藏
各行业大数据查询平台,总有用的上的时候
一、数据平台(5类)网络趋势分析6个1、5118 / chinaz——主要用户:SEO专员支持查询网站排名及发展趋势、百度收录情况等信息 2、艾瑞指数——主要用户:互联...【详细内容】
2020-07-01  Search: 数据查询  点击:(3375)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(6)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(13)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(9)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(11)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(9)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(6)  评论:(0)  加入收藏
站内最新
站内热门
站内头条