本文整理自第四范式技术日中 Akulaku 算法总监马宇翔在「高效落地 AI 工具链及开源生态」分论坛的演讲。
大家好,很高兴能和大家一起参加第四范式的技术日,做关于 OpenMLDB 在 Akulaku 数据驱动中应用实践的分享。
我是来自 Akulaku 的马宇翔。对于 OpenMLDB 来说,我们算是一个早期的关注方,也是对它提供的解决方案存有浓厚兴趣的企业方,所以今天我非常希望通过和大家分享我们的使用体验,来抛砖引玉。
公司简介
Akulaku 是一家做电商平台以及数字银行的金融科技出海公司。我们的电商平台在东南亚应该是 TOP 5,数字银行在印尼应该是 TOP 1。这样的业务和市场规模使得我们有非常多的线上的数据需要处理,同时这些数据又是用来解决电商或金融相关的实际问题,导致 Akulaku 对数据准确性和实时性以及高性能都有比较高的要求。
场景需求和架构设计
Akulaku 的数据架构如图所示。在特征计算层,有一些第三方和自研的底层工具;在模型计算层,做了一些开放架构式的整合,尽可能地构成了一个易扩增且不依赖于某种特定工具的计算模式框架。这两层负责支持智能应用,比如说把行为动作、地理位置、设备指纹,也有银行的一些反洗钱、设备风控,以及基于用户体验的智能客服、智能投顾。这些智能应用基于相同的数据驱动底座技术栈来提供服务,在设计满足上述条件的方案时我们发现了 OpenMLDB。
场景需求
作为 Akulaku 的数据部门,我们平时会面临各自来自上下游的诉求。
下游业务方会要求我们尽可能支持各种各样的功能,并且要求实时使用。我们常见的数据应用方会需要同时使用离线计算、异步实时计算和硬实时计算以满足决策需要。这些关键事件的决策不能出错,同时决策的稳定性也要有所保障。
对于上游的诉求。比如说运维部门作为资源提供方,存在成本上的诉求,整个计算体系的资源希望尽可能的少。这里的少,更多也是满足业务前提下的少,也就是要做到全局最优。要求我们做精细化,可是精细化会带来复杂度的提升,复杂度的提升又会降低稳定性。上下游的诉求存在互斥,对于我们来说是一个挑战。
对于内部的数据部门来说,因为大数据工具的频繁迭代,员工的学习成本很大。比如说 Spark 对批量计算友好,Flink 对流式计算友好。但是为了每一种计算模式都单独学一个工具,并且根据工具迭代跟进学习,还可能要适应改造去大幅改造现有系统,会导致从每一个员工到整个部门都难以获得沉淀,且非常痛苦。
在易用性这边,我们的使用方是希望整个平台的设施足够简单方便普及。既要处处可用,还要使用方式比较简单,不然各类型的服务角色比如说开发者的诉求、分析师的诉求和算法工程师就不太一样,很难都得到满足。
其次重要的是可靠性,如果系统部署困难、三天两崩,出现问题无法自查,测试时间过长都会给我们带来很大的影响。所以以上的这些问题,我们必须逐一避免或减少。
架构设计
设计需求
那以上种种情景下,Akulaku 的架构设计必须满足下面两个目标。
首先我们需要同时做一个适用于 OLAP 和 OLTP 的一些高效融合计算的方案,它需要同时实现 AI 和 BI 的数据执行,必须保证 AI 和 BI 在整个的生产线上尽可能地使用同一份数据去运行,而不是分别跑出两个中间结果,然后再得出不一样的结论,这个有很高的风险。其次使用的工具需要兼容其他的工具,拥有良好的生态。如果没有良好的生态,我们也很难把它放置在整个的架构里。
经过大量的二次开发和以上两个目标的筛选,我们明确了 Akulaku 数据架构中工具需要支持五种条件:
第一,流批一体。它的批处理和实时处理应该是一个相同的代码能执行,而且执行出来的底层也应该是相同的逻辑。
第二,高性能。因为在线上的大并发和线下大吞吐量的任务都需要做一个支撑。
第三,场景无关。场景无关的这种特性,我们需要它具备一份数据可以处处使用,然后通过一些筛选,或者是说加窗口的方式去改变它的条件。而不是说它每换一个场景,我们都要重新去做一遍全量计算,或者全量数据导入导出。
第四,语义支持。语义支持更多是因为我们的流式计算有很多的新的语义,就是像 Flink 每次版本迭代,都会根据具体的大家提的使用需求去迭代一些新的语义。那么对于这些语义,希望我们的工具也能得到一定的支持,它能做一些比较复杂的实时计算场景。
第五,工具高效。首先我们需要搭建计算框架的组件,它本身是能沉淀我们一些上线的流水线和线下分析的数据逻辑的这些能力。便于我们后续对它做一些迭代。
设计实现
最终成型的特征和数据计算架构如上图。
首先我们数据源可能会来自 HDFS、Kafka,还有其他服务语言的 SDK,或是 Nebula 这种比较特殊的图计算工具。基于这些不同的数据源,我们在中间做了流批一体化,最后主要选择了 OpenMLDB 不同的模式去实现这一套的功能,再通过中间件去屏蔽掉流批一体化的不同组件对于一个逻辑的不同实现,保证接下来的融合计算组件能得到很好的降低复杂度的程度。
在 Akulaku,接近 50% 的、使用量最大的一部分指标对实时性的要求较低,比如运营人员或者管理人员需要的数据指标或是一些对实时性要求比较差的特殊模型,可能会使用性能或性价比相对较高、更加普适的 Rocksdb 模式去做。
接下来,如果对于生产要求,准确性以及性能、计算速度要求比较高的批处理,我们就会使用背后基于 Spark FE 的 OpenMLDB 离线模式,它的性能比 Spark 要好很多倍。
如果有一些硬实时的计算,就会采用 OpenMLDB 的在线模式去做,可以做到大并发下面保持在几十毫秒级这个水平,基本上是满足 200 毫秒硬实时的门槛。
其他的一些补充,例如 Clickhouse 或者数据湖的组件,就会在指标市场或更多的数据大盘上做一些支持,这儿就不赘述了。
在融合计算方面,我们主要基于 Ray 来完成的,Flink 是我们之前的方案,但是目前是在 Ray 上去做全盘过渡。
数据应用层,首先就是因为 OpenMLDB 的离线、在线一体化,所以我们可以很轻易地去把 MLOPs 做到持续交付到持续部署中间这一步的测试自动化,可以简化非常多步骤,因为代码是一致的。有了 OpenMLDB 之后,我们就可以比较轻松地去做 AutoML,其他的一些指标市场和低代码分析,完成一些更精细化的 BI 的应用。
应用细节和使用案例
为什么最后选择把 OpenMLDB 选择放在我们的核心位置?
第一,天然地支持流批一体。流批一体是 OpenMLBD 的一个核心,或说主打功能,也是我们最刚需的功能。
第二,高性能。实测 OpenMLDB 的性能时,如果从 Kafka 写入数据最大可以做到 1 万的并发,当然这是一个三节点的集群,可能更多节点的集群会有更好的效果,也就是说 OpenMLDB 的性能还有扩展空间。在离线部分,OpenMLDB 性能超过 Spark 数倍,基本上能满足常规的一些使用。在实时计算部分,我们可以轻松地做到接近 200 的 qps,还可以保持 99% 的 70 毫秒内的计算。所以可以说,OpenMLDB 是一个非常优秀的线上和线下的计算工具,也是一个非常优秀的可以同时满足线上计算和线下计算的分析数据库。
第三,场景无关。这个特性,它是可以在内存做一个持久化,以及我们可以选择使用持久化内存版本,来确保我们数据在非常极端的情况下还是能够得到恢复。通过这种方案,我们就可以在一次地把数据写入之后,然后不停地通过 SQL 去控制计算力度,来确保我们可以不停地复用这些数据。
其次就是它自己也支持数据过期。数据过期功能,就是说可以把一些我们设定好的,过了多久不会使用的数据自动给干掉,那就会省很多的一些空间,然后提高我们的存储有效性。而且它还支持 Rocksdb 的版本,然后去做到降本增效的效果。
第四,语义支持。它支持了常见的流式场景需求,而且可以和批式的语法使用相同的一些算子。同时它也支持 UDF 或者 UDAF 一些特征工程的函数扩展。这些扩展方式对我们来说还是很实用的,因为你可以把一些特定的逻辑分装成函数使用。
第五,工具高效。我们目前是使用一些像 Airflow 之 类的工具去把整个的脚本做一些流水线的固化,然后这个固化搁置呢,OpenMLDB 在里面只需要配置一些类似 SQL 脚本就可以完成了,这个方式是比较便于实现 MLOPs。同时 OpenMLDB 也在打造和很多第三方工具的使用生态,去确保我们可以更便利地和其他工具打通。
应用思考和建议
应用思考
接下来的内容想介绍一下 OpenMLDB 的应用思考。
第一是关于标准 SQL,我们是否一定要有一个满足标准 SQL 的工具呢?我们思考的结果是:其实也不那么必要。因为标准 SQL 语法更多的本质目的是为了支持我们的逻辑一致性,但对于逻辑一致性,我们还是有其他的方案可以去实现的。比如大家如果是调用一些非标的方法或者说还未支持的方法,我们就必须调用自我实现的一些功能或者使用软件工程的一些设计模式去解决掉这些分装,或者解决掉多层复用的一些问题。
这种方式,可以为我们换来超越标准 SQL 工具更多效率吧,这个效率其实是我们目前更为需要的一个东西。以我们自己的时间来说,现在是一些复杂到可能数百行 SQL 盘活挖掘类型的任务,都是可以通过这种方案去解决掉的,它的扩展空间还是非常大。
第二就是关于质量和效能的平衡。质量和效能,一般来说我们希望它同时提升,但是更多时候它们也会有一些冲突。比如说每当提高复杂度,那质量就会下降,但是提高复杂度可以精细化整个产品的一个效能。这些时候,我们就会选择做一些错位对齐。
比如说以 OpenMLDB 的三种实现模式来说,我们会选择使用特定的模式去实现尽可能恰当的那些特征。比如说 Rocksdb 的版本,我们就会做通用的指标计算的工具。
在线计算的版本,我们就会用来做一个线上实时模型特征计算的工具。离线版本,我们就会用来做一些 T+1 的一些非常大批量数据的计算工具。就是说,每种方案我其实都是可以以特定的数据或者说场景尽可能最优匹配。
这种方式我们还运用到一些预计算、及时计算、软加载或者窗口划分之类的方案中,进一步去优化它的质量和效能的平衡。
第三是在业务和技术上面,我们也需要做一些取舍。对技术来说,希望我们的技术不产生任何的一些技术债,然后不停地去向上迭代。针对业务来说,它需要的其实就是你的功能的完整实现以及上线时间。
关于这一点,我们是之前做了一些低代码的工具去完成这个事情。如果是一个标准的低代码工具,那它可能更多节省的时间是业务人员 “拖拉拽” 的时间,这个其实并不是业务真正想要的。他们更想要的其实是缩短上线时间,这个上线时间的减少就需要看你使用的工具能否直接从持续交付进展到持续部署,这一点就是 OpenMLDB 在这儿起到的作用。就是我在这边 “拖拉拽” 完成的一套低代码工具背后实现生成的 SQL,我就可以直接应用在线上的版本去做部署。
使用细节
接下来,就是想介绍一些具体的使用细节,其实更多是一些 tips,可能会对大家使用这个工具的时候有一定的帮助。
首先就是在建表环节,我们可能会提供多种的索引定义方式。主流的方式就是使用 INDEX 里面 Key 的关键字,另外一种就是使用时间窗口里面的 TS 关键字,时间窗口的这种关键字就是用于所有基于时间的流式数据。使用 Key 关键字的,需要我们自己把这个事件进行序列化,然后定义其中可以帮助你良好地把序列拆分开的一些字段用来做一个关键字。如果定义非常成功,就会让我们使用这个工具的效果得到一个极大的提升。首先是逻辑会得到简化,其次就是性能也会提升很多。OpenMLDB 目前支持的时间字段就是两种数据类型,这个也是需要注意到的。
接下来在查询部分,当我们查询一条数据的时候,并不需要完整地把一个非常庞大的业务 SQL 传进去。我们更多的是可以说,只用到我关心的、所用到的字段和时间戳。其他不重要的可以使用一些替代值它给占位,有了这个占位之后,OpenMLDB 是已经可以正常工作。
其次就是金融场景尤其常见的,不使用当前行去计算一定时间窗口内的数据。Akulaku 使用的解决方案是,我们排除掉当前这个字典里面所在的毫秒时间戳里面的第一个字段,然后通过这种方式去把排除当前行的操作解决掉。那么里面其他的字段,因为是一个非时间戳,而且不是我们用到的字段,所以就是一个不重要的无所谓的数据,可以随便的去做一些占位。这些操作是可以比较简化排除当前行这个问题的实现,不需要做一些非常复杂的逻辑。
建议和展望
最后想和大家介绍一下,我们最后使用下来的一些建议,以及对 OpenMLDB 产品未来迭代的展望。
使用建议
关于这一块相对比较经典的使用建议的话,首先就是如果我们的逻辑它很复杂,那会导致线上验证和线下生效,这两个事没办法在很短的时间内判断出逻辑是否一致,或者说最后跑出来的结果能不能是一个数。对这种方式,我们就会建议使用 OpenMLDB 来去完成,因为它是天然消灭这种问题。
其次就是说,如果我们参与计算的数据可以按照时间或者某个索引非常完美地去切片、做窗口,那我们也是建议使用这个工具。因为它的性能会非常的高,那我们的性价比和效率就会提升到一个非常可观的一个程度。
第三部分就是说,如果我们逻辑的开发人员不是那么关心大数据领域和高性能领域的一些问题,甚至说包括 SQL 优化也不是很想考虑的话。那我们也建议使用这个工具来去做这个逻辑的开发。
就目前来说,就我们使用下来 OpenMLDB 本身的性能、底层的优化已经做得很到位了。关于 SQL 语义这块特别影响性能的实现,比如说多表联做这种,它是直接不支持的,那么也就是不会让逻辑开发人员写出来一些非常低性能的代码,可能会造成系统血崩之类的问题。
其次就是我们建议要使用好 OpenMLDB,我们希望企业内部还是需要有比较清晰的数据治理的能力。不然的话,可能我在第一步的过程中,就是导入 OpenMLDB 里面的数据可能就会相对比较乱。它更多也不是一个在内部做数据清洗的一个工具。如果要用好 OpenMLDB 的强项——计算,那我们最好是把一个尽可能清晰的,需要用它算的数据输入进去,然后可以直接执行后面的相应逻辑,而不是一直收到报错。
迭代展望
接下来就是我们认为 OpenMLDB 后面还会支持的一些功能,然后以便于更加方便我们的一些使用。
第一,就是目前看起来它是有在做一些进一步的支持异构资源,去降低存储成本之类的事情。这个操作后续的衍生,我们认为就会去做一些更进一步的精细化的使用配置。同样的一个表里面,甚至可以支持某些字段的计算,例如需要用内存版本某些字段的计算,用 Rocksdb 去做就可以了。这种方式,可以让资源的精细化管理做到一个相对比较极致的水平。只要所谓的成本和产出的 ROI 能达到更高的话,那 OpenMLDB 的应用场景其实就会更宽。
第二,目前它的数据 IO 和 SDK 支持来看,它后面还有很多可以支持的一些工作。比如说目前的离线数据导入,我们一般是使用 HDFS 或者 CSV。那还有比较新的一些数据或者说离线的数据湖,或者说在线的一些连接器,那都是它后续可以做一些实现的。
其次就是关于 SDK 的支持,我们目前在 Java SDK 和 Python SDK 使用上面,如果相对于其他一些更成熟的数据工具,我们希望能有一种像是支持 Python 多线程。或者对 Java 来说,可能就是它的生成文件形式可以更友好,或者说它可以直接有一个非常明显的开关功能,都可以帮助我们更好地去便利使用 OpenMLDB。
第三,我们期待有更多来自社区的文档贡献,比如说 OpenMLDB 有很多的宝藏功能,比如说 UDF 函数的一些实现,或者是关于在线和离线两种不同模式底层的数据如何做一致性之类的设计能够给还未入门或者说刚入门的开发者一个更加充足的介绍,那我相信它的转化和使用量也会得到更迅速的一个增长。
第四,我们认为 OpenMLDB 可以有一个更友好的 SRE 支持的设计,比如说关于数据过期是一个非常好的功能。但是如果是生产环境下的话,出了一些问题就不太好回溯,也不太好去做进一步的迭代。那这个时候,如果我们可以有一个选项是把它做一个异步转存,再或者后面再补充一些定时删除,对于 SRE 这边的排查问题或者说后续的功能迭代都会更友好一些。当然也包括比如说现在命令行日志更细的分级,或者在整个数据库级别做一些管理权限的一些支持。这些都是作为 SRE 可能会关心到的一些诉求。
关于整个 OpenMLDB,我们这边的一些建议和使用实践就到这里了。同时也非常期望能看到更多的企业来去使用它,通过共同的 “踩坑” 和“填坑”把 OpenMLDB 做成一个更好的工具。