写点什么

腾讯大咖分享 | 腾讯 Alluxio(DOP) 在金融场景的落地与优化实践

作者:Alluxio
  • 2022 年 7 月 15 日
  • 本文字数:5995 字

    阅读完需:约 20 分钟

腾讯大咖分享 | 腾讯Alluxio(DOP)在金融场景的落地与优化实践

Alluxio 导读

近期,腾讯 Alluxio 团队与 CDG 金融数据团队、TEG supersql 团队、konajdk 团队进行通力协作,解决了金融场景落地腾讯 Alluxio(DOP=Data Orchestration Platform 数据编排平台) 过程中遇到的各种问题,最终达到了性能和稳定性都大幅提升的效果。

背景

在腾讯金融场景中,我们的数据分析主要有两大入口,一个是基于 sql 的分析平台产品 idex,另一个是图形化的分析平台产品"全民 BI"。全民 BI 是一款类似 tableau 一样的可以通过拖拉拽进行数据探索分析的工具,因为不需要编写 sql,所以面向人群更广,不仅包括了数据分析人员,还有产品和运营,对耗时的敏感度也更高,而这里主要介绍的是针对全民 BI 应用场景的落地优化。

为支持日益增加的各类分析场景,今年腾讯金融业务数据团队进行了较大的架构升级,引入了 Presto + 腾讯 Alluxio(DOP),以满足用户海量金融数据的自由探索需求。

大数据 olap 分析面临的挑战

| 挑战一:从可用到更快,在快速增长的数据中交互式探索数据的需求。

虽然这些年 SSD 不管是性能还是成本都获得了长足的进步,但是在可见的未来 5 年,HDD 还是会以其成本的优势,成为企业中央存储层的首选硬件,以应对未来还会继续快速增长的数据。

但是对于 olap 分析的特点,磁盘的 IO 是近乎随机碎片化的,SSD 显然才是更合适的选择。

下图展示的是 olap 分析中 presto 对一个 ORC 文件读取的视图,其中灰色竖条表示具体的分析需要读取的三列数数据在整个文件中可能的位置分布:

| 挑战二:在多种计算任务负载,olap 分析的性能如何在 IO 瓶颈中突围

企业大数据计算常见的两种负载:

✓ ETL:数据的抽取(extract)、转换(transform)、加载(load),主要是在数据仓库、用户画像、推荐特征构建上,特点是涉及大部分的数据列。

✓ OLAP:在线联机分析处理,主要用在对数据的多维度分析上,特点是仅涉及少量的数据列,但可能涉及较大的数据范围。

虽然 ETL 的峰值会在凌晨,但其实整个白天都会有各种任务在不断的执行,两种类型任务的 IO 负载的影响看起来不可避免,再加上中央存储层 HDD 硬盘的 IO 性能约束,IO 很容易会成为数据探索的瓶颈。


| 一种流行的解决方案

面对这些挑战,目前很多企业会选择下面的这种架构:

将 olap 分析需要的热数据(比如近一年)复制到一个 olap 专用的存储中,这样不仅可以解决 IO 竞争的问题,还可以选用 SSD 硬盘,进一步加速 olap。

但是这样的架构却又引入了新的问题:

✓ 数据的边界:因为数据需要提前复制,如果需要临时分析超出约定范围的数据(比如同比去年),就会导致只能降级到中央存储上的引擎去执行,这里不仅涉及到存储的切换,也涉及到计算引擎的切换。

✓ 数据的一致性和安全:数据复制需要面对数据一致性的拷问,另外就是这部分数据的权限和安全问题能否跟中央存储进行关联,否则就要独立管控数据的权限和数据安全,这无疑又是不小的成本,这一点在注重监管的金融行业尤其如此。

Alluxio:一种可能更优的方案

重新思考我们的 olap 引擎的存储需求其实是:

1)有一份独享的数据副本,最好采用 SSD 的存储,满足更高的性能要求

2)不需要额外的数据管理成本:数据生命周期、权限和安全

所以我们首先想到的其实是在 HDFS 层面解决,Hadoop 在 2.6.0 版本的时候引入了异构存储,支持对指定的目录采取某种存储策略,但是这个特性并不能解决我们的几个问题:

✓ 不同计算负载的 IO 隔离:因为这部分对于 olap 引擎(比如 presto)和 etl 引擎(比如 spark)是透明的,无法实现让 olap 引擎访问某一个指定的副本(比如 ONE_SSD 策略的 SSD 副本)

✓ 数据生命周期的管理成本高:如果要根据冷热做动态策略管理还有大量的工作要做

数据副本其实可以分成物理和逻辑层面来考虑:

1)物理两套,逻辑两套:需要面对两份数据管理的问题

2)物理一套,逻辑一套:难以解决 IO 隔离的问题

在上面两种不可行的情况下,我们自然地想到了另一个思路:

✓ 物理两套,逻辑一套?

而 Alluxio 恰好在这个思路上给了我们一种可能性:

Alluxio 的元数据可以实现跟 HDFS 的同步,有比较完善的一致性保障,所以可以理解为在 Alluxio 中的数据跟 HDFS 是一份逻辑数据。而基于数据冷热驱逐的自动化机制给更灵活的数据生命周期的管理提供了一条通路。

这样,结合数据的预加载,结合 Alluxio 的缓存特性,不仅做到了无边界的访问中央存储的数据,同时也实现了热数据的 IO 隔离和 SSD 加速。

但区别于更流行的缓存加速的用法,我们使用 Alluxio 的方式更倾向于 IO 隔离

| Alluxio 的缓存策略选择

Alluxio 的两种主要缓存策略

✓ CACHE : 通过 Alluxio 访问后,如果不在 Alluxio 中,则会进行缓存,单位为 block

✓ NO_CACHE:通过 Alluxio 访问后,如果不在 Alluxio 中,不进行缓存

两种策略对应两种不同的存储管理方案:

【名词解释】

预加载查询:是通过 olap 应用系统登记注册的分析主题(对应库表),然后构造的简单聚合查询:select count(*) ,来触发 Alluxio 的数据加载。

最后考虑到长期的管理和运维复杂度,我们选择了路径单一容错性更高的 CACHE 方案

新的挑战

思路清晰了,但是还是有三个挑战:

1)如何让 Alluxio 只应用于 olap 引擎,而避免修改公共 hive 元数据中的数据 location

2)如何避免一个随意的大范围查询导致其他数据被大面积驱逐?

3)异构存储机型下,我们的缓存请求分配策略怎么选择?


| 挑战一:如何让 Alluxio 只应用 olap 引擎,而无需修改 hive 元数据?

因为 alluixo 的访问 schema 是:Alluxio:// ,所以正常情况下使用 Alluxio 需要在 hive 中将对应表格的地址修改为 Alluxio://,但如果那样做的话,其他引擎(比如 spark)也会同样访问到 Alluxio,这是我们不希望的。

得益于 TEG 天穹 presto 团队的前期工作,我们采取的做法是通过在 presto 中增加一个 Alluxio 库表白名单模块解决。也就是根据用户访问的库表,我们将拿到元数据的地址前缀 hdfs://hdfs_domain/user-path 替换成了 alluxio://allluxio_domain:port/hdfs_domain/user-path, 这样后续的 list 目录和获取文件操作都会走 alluxio client,以此解决 alluxio 的独享问题。

另外对于商业版本的 alluxio,还有一个 Transparent URI 的特性可以解决同样的问题。


| 挑战二:如何避免随意的大范围查询导致其他数据被大面积驱逐

利用库表白名单,我们实现了对 Alluxio 存储的数据的横向限制,但是依然存在一个很大的风险就是用户可能突然提交一个很大范围的查询,进而导致很多其他库表的数据被 evict。

因为只要采用的是 CACHE 策略,只要数据不在 Alluxio,就会触发 Alluxio 的数据加载,这时候就会导致其他数据根据 evict 策略(比如 LRU) 被清理掉。

为了解决这个问题我们采取了下面的两个关键的策略:

✓ 基于时间范围的库表白名单策略:

在库表白名单的横向限制基础上,增加纵向的基于分区时间的限制机制,所以就有了我们后面迭代的基于时间范围的库表白名单策略,我们不仅限制了库表,还限制了一定的数据范围(一般用最近 N 天表示)的分区数据,然后再结合用户高频使用数据的范围,就可以确定一个库表比较合理范围。

下面是一个样例片段参考:

"dal_base.*","dal_base.*.${yyyyMMddHH:(-720h,0)}","dal_base.*.${yyyyMMdd:(-217d,0)}","dal_base.*.${yyyyMM:(-36m,0)}"
复制代码

✓ 降低 Alluxio worker 异步缓存加载的最大线程数:

Alluxio.worker.network.async.cache.manager.threads.max 默认是 2 倍 cpu 核数,我们基本上是调整成了 1/2 甚至是 1/4 cpu 核数,这样因为查询突然增加的 load cache 请求就会被 reject 掉,降低了对存量数据的影响。

这样我们实际上就是构建了一个 Alluxio 的保护墙,让 Alluxio 在一个更合理的数据范围内(而不是全局)进行数据管理,提升了数据的有效性。

而且采用这样的策略,部分直接走 HDFS 的流量不管是耗时,还是对 Alluxio 的内存压力都会有所降低。


| 挑战三: 异构存储机型下,我们的缓存请求分配策略怎么选择?

这个也是将 Alluxio 当作一个存储层,可以独立扩展必须要面对的,新的机型不一定跟原来的一致。面对异构 Worker 存储的需求,Alluxio 已有的块位置选取策略,都会造成热点或者不均衡的问题,不能有效利用不同 worker 上的存储资源。比如:

✓ RoundRobinPolicy、DeterministicHashPolicy:平均策略,将请求平均分配给所有 Worker,会导致小容量的 worker 上的数据淘汰率更高;

✓ MostAvailableFirstPolicy:可能会导致大容量 worker 容易成为数据加载热点;而且因为所有 worker 存储最终都会达到 100%,所以满了之后这个策略也就是失去意义了。

因此 我们积极参与腾讯 Alluxio 开源社区,设计并贡献了“基于容量的随机块位置选取策略 CapacityBaseRandomPolicy”。

该策略的基本思想是:在随机策略的基础上,基于不同 worker 的容量给予不同节点不同的分发概率。这样容量更大的 worker 就会接收更多的请求,配合不同 worker 上的参数调整,实现了均衡的数据负载。

如下图所示,是上线初期的容量情况,第一列是存储容量,第二列是使用容量,可以看到基本是按比例在增长。

除了上面的三个挑战,我们还对方案中的一个问题"presto 触发查询会导致多副本问题"做了优化。因为 presto 的查询会将一个文件拆成以 split 为单位(默认 64MB)进行并行处理,会在不同 Worker 上触发缓存,实际上会对数据产生多个副本。本来我们使用 DeterministicHashPolicy 来限制副本数量,但是由于切换到了 CapacityBaseRandomPolicy,我们再一次对副本数失去了控制。因此我们做了如下两个优化:

✓ 预加载查询设置大 split(max_initial_split_size,max_split_size):使用跟 alluxio block size 一致的 split,比如 256MB,这样避免一个文件被拆成多个 split

✓ 对 CapacityBaseRandomPolicy 增加了缓存机制:避免了同一个 worker 多次请求发送到多个 worker 上,触发多个副本加载问题

最终架构

在落地过程中,为了满足实际存储需求,额外申请了 SSD 存储机型扩容了 Alluxio worker,最终采用了 Presto + 腾讯 Alluxio(DOP) 混合部署以及独立部署 Alluxio Worker 的架构,即有的服务器同时部署了 Presto worker 和 Alluxio worker,有的服务器仅部署 Alluxio worker,该架构具有计算和存储各自独立扩展的能力。

线上运行效果

我们基于某一工作日随机抽取了一批历史查询,5 个并发,由于完全是随机的,所以查询涉及的范围可能包含了部分一定不走 Alluxio 的数据(不在预设的白名单时间范围,或者没有命中),但是能更真实反映我们实际使用的效果。

测试我们选取了两个时间段:

1) 周末下午:500 个查询,大部分 ETL 任务已经完成,HDFS 大集群负载低,这时候主要看 SSD 加速效果。

2)工作日早上:300 个查询,这个时间点还会有很多 ETL,画像标签、推荐特征等任务运行,HDFS 集群繁忙程度较高,这个主要看 IO 隔离性。

测试结果如下:

闲时:

图中的横坐标是按耗时从低到高排序后的 500 个查询(剔除了最大值 213 秒),纵坐标是耗时(单位秒),其中 90 分位的耗时有 Alluxio 和无 Alluxio 分别是 16s 和 27s,90 分位的查询性能提升为 68.75%,这里主要是 SSD 带来性能提升。

忙时:

图中的横纵坐标如上一个图一致,横坐标是 300 个按耗时排序后的查询,注意:因为查询覆盖的数据范围可能超过 Alluxio 的数据范围,所以会出现极端值。

效果总结:

从测试结果可以看到:

✓ SSD 提速:即使在闲时对 50%以上的查询都有一定幅度的提升效果,在 90 分位达到了 68%的性能提升。

✓ IO 隔离优势:可以看到 HDFS 忙时,无 Alluxio 的 90 分位查询会明显上升,但是有 Alluxio 的查询非常平稳,在 90 分位到达了+294%的性能提升。

优化调优实践

| 采用腾讯 Konajdk + G1GC

腾讯 Alluxio(DOP) 采用 KonaJDK 和 G1GC 作为底层 JVM 和 垃圾回收器。KonaJDK 对于 G1GC 进行了持续的优化,相较于社区版本,针对腾讯内部应用特点进行了深度的优化,减少了 GC 暂停时间和内存使用。


| 利用腾讯 Kona-profiler 定位高并发访问 Alluxio Master FGC 问题

当出现业务海量并发查询请求场景,Alluxio Master 出现了频繁 FGC 的情况,并且内存无法大幅回收,导致 Alluxio Master 无法正常提供服务,影响业务使用。

我们获取了 JVM heap dump 文件,使用 kona-profiler 进行分析。


使用 kona-profiler 快速发现问题的瓶颈在于:短时间内出现了大量未被释放的 Rocksdb 的 ReadOptions 对象,这些对象被 Finalizer 引用,说明 ReadOptions 对象可以被回收了,但是在排队做 finalizer 的函数调用,进一步定位发现,ReadOptions 对象的祖先类 AbstractNativeReference 实现了 finalizer 函数,其中的逻辑又过于复杂,因此回收较慢,这在 7.x 版本的 rocksdb 已经修复。

但由于版本升级跨度过大,我们采用另一种办法解决该问题。配置腾讯 Alluxio 的 alluxio.master.metastore.block=ROCKS_BLOCK_META_ONLY,支持把 blockLocation 独立放置于内存管理,而 block 信息使用 rocksdb 管理,这样从根本上避免了原本海量获取 block 位置操作,构造海量 rocksdb 的 ReadOptions 对象的问题。

升级改造后。


Alluxio 侧,在压测的情况下,999 分位从原来的 10ms 减少到了 0.5ms,qps 从 2.5w 提升到了 6.5w;

正常负载下升级前 rpc 排队情况:

正常负载下升级后 rpc 排队情况:

Presto 侧:对于涉及分区很多的查询场景,比如大范围的点击流漏斗分析,在一个基准测试里,从 120 秒减少到了 28 秒,提升了 4 倍。


| 周期性出现 50 秒慢查询问题参数优化

一个查询多次执行耗时差很多。大部分在 7 秒左右,但是会偶尔突然增加到 50 秒,就是某个数据读取很慢,测试的时候集群的负载还是比较低的。

下图是慢查询时 Presto 的调用栈

结合源码,可以看出此时 Alluxio 客户端认为拿到的 BlockWorker 客户端是不健康的。

判断健康的判定标准为:不是 shutdown 状态,且两个通信 channel 都是健康的。

根据上下文,可以判断,目前不是 shutdown 的,那么只能是两个通信 channel 不健康了。

进一步结合源码,定位在 closeResource 过程中,会关闭并释放 grpcConnection,这个过程中会先优雅关闭,等待超时如果未能优雅关闭则转为强制关闭。

因此,规避该问题,只需要修改调小配置项 alluxio.network.connection.shutdown.graceful.timeout 即可。


| Master data 页面卡住的问题优化

Alluxio Master 的 data 页面,在有较多 in Alluxio 文件的时候,会出现卡住的问题。这是因为,打开这个页面时,Alluxio Master 需要扫描所有文件的所有块。

为了避免卡住的问题,采用限制 in Alluxio 文件个数的解决办法。可以配置最多展示的 in Alluxio 文件数量。

总结展望

✓ 腾讯 Alluxio(DOP) 支持 BlockStore 层次化,前端为缓存层,后端为持久层,同时,blockLocation 这种不需要持久化的数据,不需要实时写入后端持久层,只需要在前端缓存层失效的时候才需要溢出到后端,该功能正在内部评测。

✓ 腾讯 Alluxio(DOP) 作为一个中间组件,在大数据查询场景,遇到的性能问题,在业务侧,需要业务团队不仅对自身业务非常了解,对 Alluxio 也需要有一定的了解。在底层 JVM 侧,需要 JVM 专业的团队采用专业的技术进行协作解决,从而最大限度的优化,使得整体方案发挥最优的性能。

✓ 这是一次非常成功的跨 BG,跨团队协作,快速有效的解决腾讯 Alluxio(DOP) 落地过程中的问题,使得腾讯 Alluxio(DOP) 在金融业务场景顺利落地。

用户头像

Alluxio

关注

还未添加个人签名 2022.01.04 加入

Alluxio是全球首个面向基于云原生数据分析和人工智能的开源的资料编排技术!能够在跨集群、跨区域、跨国家的任何云中将数据更紧密地编排接近数据分析和AI/ML应用程序,从而向上层应用提供内存速度的数据访问。

评论

发布
暂无评论
腾讯大咖分享 | 腾讯Alluxio(DOP)在金融场景的落地与优化实践_腾讯_Alluxio_InfoQ写作社区