乐趣区

关于算法:第四范式OpenMLDB在金融风控数据库重计算优化实践

近日,在 DataFunSummit:智能金融在线峰会上,第四范式平台架构师陈迪豪以《OpenMLDB 风控数据库计算优化》为主题,重点介绍了第四范式开源机器学习数据库 OpenMLDB 在金融畛域的利用,以及底层时序特色的解决、窗口的计算优化细节等,让用户能够了解风控数据库的技术架构,理解底层基于窗口的计算性能优化点,以及性能优化的实现细节。

一. 风控场景特色设计

基于机器学习的智能风控数据库,逐步取代了人工审核和专家规定,成为准确性更高、更牢靠的风控系统,前面会介绍一下风控场景下智能风控平台的设计以及它的特色设计。

首先是风控系统的演变,大家曾经理解了,从最早的人工审核,获得了肯定的成果,但老本较高,效率较低。进入 21 世纪,行业内开始利用计算机自动化能力与专家规定相结合的形式,解决效率低、自动化较差的问题,然而它同时呈现容易误杀,用户体验较差,无奈用于事中拦挡,准确率低的问题。近几年,各大金融机构和互联网金融企业的风控系统开始采纳基于机器学习的形式来实现。

首先利用从海量的数据中训练失去机器学习模型,比如说传统的 LR 模型,还有更简单的 DNN 都能够实现更高准确率,还能够依据不同的利用场景,实现千人千面的模型预测。机器学习的特点就是隐蔽性更强,准确性更高,迭代速度更快,也逐步成为智能风控系统必不可少的技术撑持。

从成果来看,在某国有银行的线上非自己交易欺诈防控场景中,OpenMLDB 上线当前在召回率 0.5 左右的状况下,预测准确率晋升了 316%;在事中交易欺诈检测场景中,相比应用专家规定的事中反欺诈检测,误报率在召回 54% 左右的时候,整体误报率降落了 33%。

智能风控系统是联合过来的一些专家规定和最新的机器学习模型来实现的一个平台。蓝色局部是银行外部已有的业务零碎,有黑白名单,有一些内部数据如时序数据等都能够通过数仓导到咱们决策平台,它不会全副依赖模型,而是联合专家规定,由规定引擎和模型预估服务来独特合作,做出最终的决策。

模型局部是传统的机器学习流程,比如说离线数据局部,实现数据导入和数据预处理、特色抽取、模型训练以及后续的模型上线以及模型自学习、模型更新等等性能。

这是一套残缺的智能风控系统,咱们明天介绍的是 OpenMLDB 在这套零碎里最底层的离线数据管理以及在线的特色查问服务。

风控场景下简单的特色工程

咱们看一下风控场景它的特色有哪些特点,首先是用户的交易信息以及用户的属性是十分重要的。时序特色在风控场景外面也是特地重要的,用户在不同的窗口,前一天、前七天、前一个月、前三个月,这些窗口都蕴含了很重要的信息特色,咱们对于不同的数据,因为它的交易次数,交易的金额在不同的历史窗口外面,也是须要计算出不同的特色过去。

还有就是交易的金额最大值和最小值,交易的地点信息等,都蕴含一些间断行为特色。所以,咱们在构建一个风控场景下的机器学习模型时,特色设计是比较复杂的,须要思考特色时序相干的计算,最大的难点在于这些特色由科学家设计进去当前,须要在在线零碎从新实现。

离线的实现传统做法是用 OLAP 或者 MPP 零碎,Spark、Flink 等自身就反对 SQL 规范的滑动窗口。但在线是很难实现的,每一个离线的特色都须要翻译到在线。如果咱们的建模计划批改了或者新增一些特色,在线也须要做新增特色的开发。新增在线特色开发与离线基于 MPP 零碎是两套计算逻辑和执行引擎,表白优化也不一样,须要大量的人工做离线在线的特色一致性校验。

OpenMLDB 为特色计算提供更优的撑持

OpenMLDB 能够解决这个问题,它针对 AI 场景做了特色优化,既实现了离线存储特色计算的优化,还实现了在线业务在毫秒级别的实时查问。

首先,OpenMLDB 是针对 AI 场景的特色计算引擎,能够面向机器学习利用提供正确高效的数据供应,无论是离线数据还是在线数据,底层都有一致性同步的,这个与后面提到的 HTAP 有点相似。然而咱们的在线数据是一个高性能的内存时序存取接口,能够毫秒级别实现时序数据供应。要实现在线离线特色计算一致性和存储一致性,这是由对立的执行引擎来实现,由对立的基于(LLVM JIT)实现的 SQL 优化器,离线和在线应用雷同的执行引擎,能力把离线在线的特色算进去,而不须要人工校验翻译,同时也反对了机器学习场景的非凡批表操作,以及非凡的特色抽取函数等。

其次,OpenMLDB 也是高性能的 OLTP 和 MPP 执行引擎,反对高性能在线时序数据的读写和复原。它的读写性能十分高,而且是针对时序数据做优化,这是目前业界无论是 OLTP 还是 TSDB 这些时序数据库都不能达到毫秒级别的性能。在硬件的优化与反对上,OpenMLDB 可在 Memory 和 PMEM 存储介质上应用,PMEM 是英特尔提供的全新存储介质,它的老本会比内存更低,而且是一种可长久化的全新存储介质,咱们针对 PMEM 也做了优化,数据恢复速度是它原来的十倍以上。此外,OpenMLDB 离线批处理性能比业界支流 MPP 零碎晋升 6 倍以上。大家都晓得像 Spark、Presto 等分布式系统,自身的性能优化是十分好的,有很好的执行引擎,然而它没有针对 AI 场景做非凡优化。下图是咱们理论场景验证的性能图,纵坐标是运行工夫,OpenMLDB 比咱们测试的最新版 Spark 性能晋升十倍以上。

二. 并行计算优化

OpenMLDB 是如何为这些风控特色做计算优化的呢?首先从并行计算优化开始介绍。大家应用 OpenMLDB 进行大规模特色抽取的时候,相比业界支流的 MPP 零碎有数倍的性能晋升,得益于底层计算优化实现。

在大规模数据处理系统里,在 2000 年开始呈现了 Hadoop,2010 年当前开始风行 Spark、Presto、Flink 等,能够应用内存做 Shuffle 防止落盘,其计算性能相比 Hadoop 又数倍甚至数十倍性能晋升。现在,第四范式 OpenMLDB 在一些大规模数据处理上,会比 Spark 和 Presto 性能晋升更多,以下会具体介绍底层的优化细节。

OpenMLDB 的右边是一些外围零碎,反对数据引入,它的外层用户有 JDBC 或者 Pytho SDK 等编程接口,底层是自研实现的 SQL Parser,也蕴含自定义的一些 Optimizer,后端实现了基于 LLVM 的执行引擎,它能够针对不同的硬件做优化,比如说 X86、ARM 架构等,当前也能够基于 LLVM PTX 后端实现 GPU 硬件加速。

底层包含两个执行引擎,一个是 Realtime executor,这个是跟传统的 OLTP 零碎一样,能够提供超高性能的时序特色读写性能。第二局部是 Massive parallel executor,和传统的 MPP 零碎相似,反对 OLAP 的大数据处理场景。底层是对立的存储引擎,应用 OpenMLDB 来做特色抽取,还反对导出开源的机器学习数据格式,比如说 Tensorflow、LightGBM 都能够间接应用 OpenMLDB 的数据做模型训练。因为离线在线一致性等个性,每个特色不须要在线做额定的翻译开发,能够间接上线了。

多窗口并行优化

为什么 OpenMLDB 的批处理模式比最新的 Spark 3.1 还能快数倍?第一个优化点是多窗口并行优化。

下面是一个简略的 SELECT 语句,依据 W1、W2 做窗口聚合计算,这两个窗口定义是不一样,如果一样的话,生成的逻辑打算就会做合并优化。然而因为它的 Partition key 不一样,这个 SELECT 翻译进去的逻辑打算就是和右边一样的。首先数据是从 T1 表过去的,下面有两个 Window 的串行计算,简直所有开源分布式数据处理框架都是这样实现的。这种实现的难度较低,遇到多个窗口也是串行计算,把 W2 的后果和 T1 须要的列筛选进去再做 W1 就能够了。

然而,在这里会呈现资源节约的状况,因而还有很大的优化空间。这两个窗口计算自身不肯定会用满所有的计算资源,实践上应该能够并行计算来实现更高的性能,如果有一个窗口有数据歪斜的问题,即时第二个窗口很快就能够实现,它也必须等第一个窗口做完才能够开始。所以,它的整体计算工夫是间接累加的,W1 须要多少工夫,W2 须要多少工夫,加起来就是它的总运行工夫。

OpenMLDB 多窗口并行优化的实现,会把串行的构造做成并行化,DataProvider 节点右边和左边别离是两个 WindowAgg 节点,这两个节点没有相互依赖,运行时只有资源足够能够并行计算。当然即时实现了并行计算,优化后它的后果与串行执行后果也是统一的。

这种并行是怎么实现的呢?后面用户通过咱们的 SQL parser 编译,能够失去一个优化后的逻辑打算。最底层是一个 DataProvider 节点,优化后是一个并行的构造。咱们执行的时候先把须要的数据取出来,而后再做第二个 WindowAgg,它们之间并没有相互依赖,数据也不须要读两份,而是都依赖于 DATA PROVIDER 节点。为了实现并行窗口数据的合并,须要再加一个 JOIN 节点。后面的简略列筛选节点,自身没有性能耗费,只是在逻辑打算里示意我计算时须要输出哪些列,在理论计算的时候,它不须要对整行数据进行编解码和传输。

数据过去当前咱们把两个节点数据做并行计算,计算后通过拼表操作拼接进去,最初再 PROJECT 须要的列,除了做 Window 咱们尽管还减少了拼表操作,但优化了多个 Window 的执行。如果有间断的 Window,咱们就能够把它都做成间断的并行优化。

尽管实现逻辑看起来比较简单,只是把串行构造并行化,但当初大家都没有这样实现,次要起因是这种优化的实现细节还是比较复杂的。咱们晓得像第一个窗口,在做 W1 窗口聚合的时候,首先须要对于窗口的 name、age 这些列保留进去,有这两列能力做分区排序,还有 age 须要做聚合。

W1 和 W2 的要求是不一样的,每个窗口计算出来只保留 age 列是不够的,实际上是这种串行的执行,T1 做完了 W2 窗口的时候,并不是只保留 W2 算进去的列,还保留 W1 列,甚至所有的须要列,输入 W2 当前才能够做 W1。做并行的时候,每个窗口只须要输入它本人的列就能够了,然而窗口的分区键是不一样,分布式数据外面每个分区内的数据也不一样,不能间接依照雷同程序做简略拼接,因而实现上咱们有一些新增列的操作,对所有数据都会给它调配一个索引列。

代码实现就须要八步,每一步都绝对简单。这里简略介绍一下,大家能够从 OpenMLDB 开源的代码外面能够看到具体的实现(OpenMLDB 开源地址:https://github.com/4paradigm/…)。

首先是拿到一个逻辑打算,咱们就要给它做剖析,咱们要晓得哪些 Window 节点是须要并行优化的,而且并行优化的节点列数不一样,这个节点在解决的时候,实现的办法和一般 Window 也是不一样的。

  1. 遍历 LastJoin1 节点,找到所有 ConcatJoin 节点,这里只有 ConcatJoin3 节点,存到一个 List<PhysicalJoinNode> 中。
  2. 以 ConcatJoin3 节点开始,进行先序遍历,这时开始打标记,创立一个惟一的 index column name,设置到标记的 Map 中。
  3. 遍历 ConcatJoin3 的子节点,每一个节点都设置标记的 Map,其中 key 为 node id,value 就是后面生成的 index column name。
  4. 在遍历子节点的过程中,还须要查看找到 ConcatJoin3 的下一个公共子节点,也就是这里的 SimpleProject6,并且设置到标记 Map 中。
  5. 在理论运行节点时,通过后序遍历逻辑打算来执行,对于 SimpleProject6 节点,在运行右边的逻辑时须要提前依据标记 Map 来增加一个 index column,这时后果不能缓存,因为在计算左边时不能返回增加 index column 的 DataFrame。
  6. 两头节点如 WindowProject4 和 WindowProject5,依据标记 Map 的信息理解到曾经增加了 index column,在做窗口计算和编解码时须要去掉 index column。
  7. 在 ConcatJoin3 节点计算时,则能够应用 last join 或者 left join 实现,并且须要依据标记 Map 把 index column 列给去掉。
  8. 其余节点,如 LastJoin1、SimpleProject2、DataProvider7 和 SimpleProject8,都按旧逻辑失常运行。

须要留神的是,第六步是所有带索引列的输入。如果还有其余节点,须要的是没有索引列的输入,这个节点会有两个输入。WindowProject4 和 WindowProject5 这两个节点曾经打了标签,晓得本人是什么节点,在计算的时候须要去掉索引列。因为在这个节点,自身没有被优化之前,它的计算是输出多少列就解决多少列,然而在被并行优化当前,它在执行 C 函数的时候须要先去掉索引列,然而输入的时候又要把索引列加进去。

在下面的 SQL 例子,W1 和 W2 只做一个特色的计算,他们只输入各自的后果列和一个索引列。拿到这两个输出表后咱们要做一个拼表操作,因为他们都有一个惟一的索引 ID,即时做完窗口计算后索引也是不变的。咱们基于后面的索引列,做一个拼表,用 last join 或者 left join 实现即可,这两个节点不受优化影响,执行逻辑和以前一样,无论是输出还是输入,最终的表和之前的未优化是一样的。

咱们看一下多窗口并行优化的成果。首先从逻辑上就能够看到,如果你有两个窗口,计算资源足够的话,两个窗口并行计算,它的计算耗时应该是能够缩小运行工夫较短的那个窗口的,在并行优化当前短的窗口耗时就被忽略了。相似木桶效益,整个用户的耗时瓶颈是在最长的窗口外面。再加上做 LLVM 的窗口计算逻辑优化,咱们最终优化后的工夫就能够降到很低了。

并行优化性能如图所示,这里列举了三个性能测试场景,和最新的 MPP System 进行比照,基于窗口并行优化后的 OpenMLDB 与开源 MPP 零碎性能能够是原来的 5.3 倍,相比 OpenMLDB 自身性能也在 2 倍左右。

三. 窗口歪斜优化

OpenMLDB 在窗口数据歪斜上也有大量优化实现。首先介绍一下数据歪斜和窗口数据歪斜的根底,数据歪斜是大数据处理场景下常见的一个景象,某一个分区或某一个 key 的数据量十分大,和其余的分区数据量不成比例。例如用户在统计这种游戏用户信息的时候,针对游戏用户的男女做一个分区解决,很可能就呈现歪斜的状况,比如说男性用户会比拟多,而这个数据因为是依照性别来分区的,性别的类型也不会有很多,分区数就会很少。也就导致咱们做分区聚合的时候,并发度也会非常少。

数据歪斜会导致数据分区和其余分区的运算后果产生较大差距,换句话说就是歪斜数据分区的计算工作与其 cpu 资源重大不匹配。最终会造成多等一的状况——多个小数据量的分区计算结束后期待歪斜的大数据量分区,只有歪斜分区计算结束能力输入后果。这对效率来说是微小的劫难。比如说 64 核,每个分区繁多解决,每个工作是 20 个核,工夫就比拟长。多个小数据分区结束后须要期待大数据辨别,只有歪斜分区计算结束能力输入后果,性能是无限的。

数据歪斜有几个计划。比如说每个分区做一个(reduce),分区之间没有关系,我给数据分成 128 个分区,这样就能够把所有的 CPU 资源用满了,每个分区用完了当前做聚合就能够失去后果。

在机器学习的特色计算中,窗口计算下呈现数据歪斜,某个分区数据过多,产生歪斜问题的时候,是不能够应用咱们方才说的传统的数据歪斜优化计划的。咱们通过数据加一个前缀,前缀能够分出更多的分区,为什么不能够这样做呢?因为窗口的计算是依赖于前后数据的,是基于某一个数据的前后数据做聚合。如果咱们把数据强行分成男性用户十个分区,第一个分区计算有可能是对的,然而第二个分区依赖于上一个分区的数据,所以在传统的实现上,无论是 SparkSQL、FlinkSQL 等等都没有对这个数据做优化。

OpenMLDB 提出了一个基于 Spark 的窗口数据歪斜优化计划,对数据做一个裁减,如果不裁减间接分区的话,会导致不同数据计算的窗口不准,后果也不统一。咱们在做完了数据裁减当前,再依据新的分区键及工夫片做新的分区,并行度就会更高。

这是一个窗口歪斜的典型场景,在咱们的训练数据外面,数据只有两个性别,男和女。在这个场景下,咱们对男和女做一个 Window 特色的话,分区键只有两个值,实际上只有两个分区。也就是说这一个 SQL 场景,咱们基于分区键来做聚合,实现上只会有两个分区,无论你的 Hadoop 集群有一百还是一千 CPU,Spark 工作只能分成两个 Task 去解决,整个工作耗时就在歪斜分区上了。

窗口数据歪斜优化计划,常见的是做重分区。这里也是相似的原理,如果分成四个分区当前,并行度就会更高。咱们常见的办法是加前缀,比如说对所有男性,咱们加不同的前缀 1 和 2。加完前缀当前,咱们对加完前缀的 key 再做分区,并行 1 的场景并行就能够变成 2,这是传统的解决方案,但并不能解决窗口计算,也就是不能解决时序特色计算的歪斜问题。

假如这是一个时序特色,输出和输入都是四行,每一行假如它的窗口是某个用户前两条数据,到本人这一条数据,它的窗口数据先进行分区。第一条数据后面没有数据,计算是正确的。第二条数据它只有一条数据,而且在这个分区外面能够找到这个数据,前两条后果是统一的,第三条开始就不统一了。在本来的表外面,都是男性,后面有两条数据,应该在聚合计算的时候,应该要蕴含前两个数据。然而咱们做了重分区当前,在这个分区外面后面是没有数据的,窗口计算只计算了单条,输入后果优化前后是不统一的。

OpenMLDB 的解决方案是通过四步来实现。从最终的后果优化能够看到,优化前是并行度为 2,优化后并行度为 4,至多能够晋升一倍。

第一步是数据评估,首先统计一下这个数据是否有数据歪斜,假如有数据须要从新分区,咱们把每个数据从新分成多块,这个例子就是先统计晓得这四块外面在 20%、50%、70% 的数据是什么,而后能够把数据平均分成多块,每一块的数据量都是靠近的。而计算每一块的 25% 是多少,须要全量的数据统计,这里能够做一个近似的查问,查问到每一个数据的排序键 25% 的地位的值是多少就能够,近似计算性能也比全量统计好很多。

第二步是数据标记,拿到数据分布表当前就能够对数据打标记,这里应用的 Join,做一个拼表的操作。把它加进去依据每一个数据针对它的 PERCENTILE 做剖析。比如说男和女的数据,平均分成四份,每一行和几个百分比值做比拟,第一条分到第一个块,前面数据别离分到第二、第三、第四个块。有了新增的增加列,我晓得每一行数据是第几块当前,就能够对每一块数据做裁减了。

第三步做数据裁减,第二个分区做窗口计算时,很可能须要用到第一个分区的数据,咱们在裁减第一个分区的数据时,加一个筛选条件为 ID 小于 2,把符合条件的列选出来,顺便设置一个标记位,原始数据不变并做一次 Unicn,最初屡次 Unicn 当前表数据量会变多,原来是十行,裁减后是二十行。

第四步是数据的重分区,很原来的不一样,原来咱们只有男和女两个分区。咱们以男性这个分区为例,假如说这里并行度为 4 的话,每个分区所做的窗口的聚合计算,它的数据都是足够的,不会漏数据。和原来的表相比,男性原来能够只分成一个分区,通过优化当前能够分成四个。

第五步做重分区后的滑窗计算。本来每个分区有多条数据,例如输出两条就应该输入两条,这里如果间接输入多条的话,最终的输入后果就跟原来优化前不统一了。因而咱们做了一个 Window 计算的优化,通过数据裁减进来的数据,咱们不让它输入后果,它只参加滑动窗口的计算,底层会退出窗口数据的队列中。前两条数据只是为了算第三条而存在的,自身不须要输入的。最终在执行的时候,会把前两条数据放在窗口外面,然而不输入,第三条数据做窗口计算的时候,能够拿到后面的后果,输入一个正确的后果。因而,才能够保障输入的后果数据,例如这里的男性分区一共是五条,跟优化前也是一样的。

通过窗口歪斜优化后,OpenMLDB 比照了最新版本的 SparkSQL,自身 OpenMLDB 相比 Spark 就曾经有四倍的性能晋升了。加上歪斜优化当前,相比 Spark 的倍数晋升更多,相比于没有歪斜优化,晋升也比拟显著。此外,歪斜优化资源越多的话,性能晋升也会更加显著。

四. 计算优化汇总

最初一部分给大家介绍一下其余的优化细节,汇总一下风控场景下的计算优化计划。第一个是原生的拼表实现,假如 T1 和 T2 这个表要做拼接,然而心愿拼接后主表数据量不变,不会因为拼表后某个数据的样本量变多,这是一种非凡的拼表需要。咱们能够基于 Spark 来实现,然而它的性能会比拟差,OpenMLDB 能够实现靠近一百倍的性能晋升,底层则是基于 Spark 源码批改反对的新型原生拼表实现。

第二个是内存 ZeroCopy 优化,OpenMLDB 底层执行引擎是应用 C ++ 实现的,所有的函数计算都不必 JAVA 的实现了,但 JAVA 对象自身是没方法通过指针来获取数据的。

咱们做得比拟极致的对接了 Spark UnsafeRow 格局,尽管 Spark 是基于 JAVA/Scala 实现的,然而它外部有一种内存管理机制,咱们实现了对 Spak 行对象内存布局的兼容。右边是 Spark UnsafeRow 的内存布局,每列的值都是依照特定的格局存储。咱们做了一个内存的兼容接口,能够间接在往 UnsafeRow 的内存进行读写,每一列通过指针的偏移计算就能够失去列的值。实现了这个性能当前,咱们就不须要对 Spark 的行做编解码转化了,性能晋升也是很显著的。在一千列的 Columns 的时候,运行工夫能够缩小一半左右。

最初欢送大家继续关注第四范式 OpenMLDB:

OpenMLDB 开源地址:https://github.com/4paradigm/OpenMLDB

退出移动版