介绍:今天分享的话题是《Kyuubi 在小米大数据平台的应用实践》,主要分为四个部分:基于kyuubikyuubi的一些新特性在业务场景中的应用01Kyuubi在小米的落地过程第一个话题:分享Kyuubi在小米大数据平台的落地过程和实现路径。背景介绍先介绍一下背景。小米的大数据系统也在不断更新迭代。随着业务结构、组织结构和技术结构的调整,一些内部大数据平台逐渐出现:出现了多个基于SQL的大数据平台。服务,服务于各个业务部门,各自的定位存在一定差异,给用户带来了困扰。选择哪个平台比较好,我们在用户支持的过程中发现,同一个业务可能需要跨越多个数据服务平台,过程繁琐。底层表资源的使用有多套账号和权限体系:a.MySQL/Doris:系统自带的User&Password认证和权限系统b.基于Kerberos身份验证和Sentry权限系统的Hive/Kuduc.Talos是基于小米内部平台组织和团队的认证授权体系,给用户使用和管理带来了困扰。没有统一的资源管理和权限管理视角,底层系统服务账号会直接暴露给用户,存在安全隐患。搭建一站式大数据开发平台以上现象直接导致以下问题:①对于用户:平台多、系统多,用户体验差,数据开发流程长,难以快速上手。开发管理成本高,资源成本结算和任务管理没有统一的视图。②平台方面:各有侧重,不能完全覆盖大数据场景下的能力需求。同时,也存在能力重复建设,造成资源浪费的问题。问题排查维护难度大,需要大量人力解决。面对数据平台使用难的问题,提出了构建统一易用的大数据服务平台的总体目标。整体架构能力围绕数据链路解决方案、数据仓库解决方案、数据服务解决方案构建,提供统一的元数据管理和权限管理体系。在这样的背景和动因下,数据统一入口服务成为了一个非常重要的能力,主要解决:用户易用性(一致的入口体验)SQL流量治理(代理多引擎)数据访问的安全控制(入口收敛和安全风险降低)小米SQL服务发展历程从以上背景问题可以看出,小米有几套大数据处理的SQL服务入口,一般都是围绕经典的SQLOnHadoop架构体系构建的。从ThriftServer逐步演进到向上抽象一层的SQLProxy服务,在底层集成Hive/Spark/Doris等引擎,提供对ETL作业和Ad-Hoc查询的支持。分离出来的SparkThriftServer实现模块作为一个独立的SQLProxy服务,提供:ETL场景下的HiveServer和SparkAPP代理(非驻留)Ad-Hoc场景下的STS、Kylin、Druid代理从这里可以看到SQLProxy和KyuubiServer的定位很相似,但是有很多不足:a.SQLProxy并没有完全去掉STS的实现,而是通过反射的方式复用。代码耦合度高,依赖特定版本的Spark,升级难度大b.底层引擎代理层不统一抽象,难以适配其他引擎,对底层引擎的扩展性差c.无法在本地调试,依赖Hadoop配置,在办公和业务环境网络隔离的情况下,完整的功能测试和调试必须在开发机上完成,开发部署路径基于Kyuubi构建统一SQL入口(一)Why选择Kyuubi通过上面的分析,我们发现在业务和架构上都存在一些需要解决的问题。①业务:在重建统一的大数据系统的驱动下,构建统一的SQL入口服务势在必行。需要更快的分析引擎,这里我们选择Trino。一套易用、高可用、持续演进的服务架构,提升大数据研发生产力。SQLProxy架构需要升级:完全兼容HiveThrift协议。松散耦合的实现,基于对STS实现的完全剥离。灵活可扩展的代理多引擎适配。Kyuubi的优势在于:全面兼容STS和HS2、高可用和资源隔离、清晰简洁的架构、可测试、可维护和可扩展的社区、高质量的实施和行业生产环境。SQLProxy和Kyuubi的架构非常相似,切换成本低。在业务需求和架构升级的驱动下,我们选择了Kyuubi。(2)架构升级升级过程和效果符合我们的预期。可以看出架构比SQLProxy更加简洁,底层引擎扩展非常容易,并且可以在本地进行测试和调试,大大提高了开发效率。从新架构开发到上线,两周内顺利完成迁移。升级新架构的效果也非常明显。与之前的架构相比,在代码质量、服务稳定性、可维护性和可扩展性方面都有显着提升:多引擎代理能力(主要支持Spark/Trino/Hive/Doris)。基于数据平台工作空间的系统在KyuubiServer端实现权限验证和资源隔离。更规范的HiveThriftAPI支持,完美兼容各种生态可视化工具(Redash/Datagrip等)。(3)统一SQL服务现状经过半年的迁移推广,每天有效的SQL处理量从5W增加到现在50W的规模,占到整个SQL流量的80%。尤其是SparkSQL的流量半年增长到30W。大体流量分布:Spark36w/Trino12w/Hive2.5w各引擎请求耗时:Spark和Trino不相上下,平均延迟30秒左右,P50在5秒左右。Hive的执行效率明显低于以上两种引擎,而且与Hive相比是大任务相关,ETL太多。目前KyuubiServer每天承载100w左右的真实SQL流量,可用性依然可以达到99.9%以上,非常稳定。02创建易用、易维护、高可用的Kyuubi服务构建满足业务需求的Kyuubi(一)整体架构总体架构及流程主要分为入口服务、鉴权与权限适配、底层引擎管理和服务可观察性:Kyuubi基于Server,构建SQL统一入口服务。KyuubiEngine作为SparkSQL执行引擎层。独立的引擎管理器服务管理各种计算引擎。KyuubiServer层集成Ranger服务,支持基于数据平台的统一权限验证。对Trino/Hive/Doris引擎的扩展适配服务指标和审计日志的可视化(2)用户与工作空间(workspace)粒度交互,保证计算资源隔离存储资源(表)的安全。类似于KyuubiGroup的多租户,我们这里将其扩展到其他引擎。一次性完成交互过程:WorkspaceA下的用户使用平台下发的Token,选择各种客户端工具,向引擎提交SQL查询,KyuubiServer会自动将用户SQL提交给空间所属的计算引擎确保用户使用资源隔离。虽然它和其他工作空间的用户有相同的入口,但是资源的使用是隔离的。KyuubiServer服务不会专门执行SQL,同一个入口服务不会有太大的压力。提高用户端易用性(1)表坐标统一认证,统一去Kerberos化,采用平台统一Token方式解决:Kerberos接入过程繁琐,普通用户难以理解kerberos机制,以及很难解决问题。同一账号下用户扩容问题的审计和跟踪,无法准确定位用户个人表资源命名统一规范。小米有多个地区和多种数据源。如果使用统一的SQL入口服务,需要规范SQL语句的表名,避免冲突,统一管理:使用Catalog.Schema.Table作为三级表名,表名唯一。KyuubiServer支持JDBCURL预置Catalog/Schema,兼容之前的SQL在二级或一级表名结合URL和SQL建表完成用户认证的三级表坐标(二)KyuubiEnginepublic资源池引入KyuubiEngine公共池主要解决用户首次进入空间提交SparkSQL的查询性能问题。根据上述用户提交的SQL分析统计,50%的SQL查询延迟在5秒以下。在没有预分配资源的情况下,提交查询的用户会冷启动Kyuubi引擎,这是Kyuubi当前的机制。由于小米Yarn提交一个APP的延迟是分钟级的,用户一个简单的秒级查询都会延迟到分钟级,非常难受。因此,随着KyuubiEnginePool的实现,对于没有提前配置和指定资源的工作空间用户,SQL将被路由到预先启动的KyuubiEnginePool,以加快用户的查询速度,提升SQL查询体验。升级Spark2.X到KyuubiEngineKyuubiEngine目前只支持Spark3及以上版本。我们之前的内部版本是Spark2。在升级到九尾引擎之前,我们做了相关的对比测试。在Kyuubi架构和SQLProxy架构下,有明显的性能提升:在TPC-DS标准测试集上,P50延迟有75%的性能提升,长尾性能与SQL代理。在实际业务场景中,P50延迟也有37%的性能提升,长尾与SQLProxy基本一致,即升级后的KyuubiEngine在大多数情况下性能优于Spark2,不会整体上比Spark2差。.Kyuubi服务器容器化在KyuubiServer的高可用中,使用容器化来替代目前KyuubiClient的高可用模式,通过ZK进行服务发现:将KyuubiServer服务部署在K8s充分利用K8s的弹性来保证高可用。KyuubiServer和KyuubiEngine的部署完全解耦,作为独立的ThriftRPC代理服务和HTTP服务,去除了Hadoop相关的配置环境依赖,和普通业务服务一样使用LVS进行流量负载均衡。同时,借助内部K8s平台的CI/CD能力,实现KyuubiServer服务的全自动灰度发布,支持一键升级和扩缩容。基于Workspace的计算资源管理(一)EngineManager由于之前已经实现了对SparkEngine的管理服务,我们直接将KyuubiEngine的管理从KyuubiServer中分离出来,形成一个单独的EngineManager服务,负责对SparkEngine的生命周期管理引擎。配置上下文管理,同时提供服务发现和负载平衡功能。为管理门户提供引擎配置和生命周期管理。为KyuubiServer提供SQL路由功能。提供运维可视化监控能力,包括Engine服务状态、资源使用情况、繁忙度等,快速运维。用户提交SQL的流程:首先经过KyuubiServer入口的身份验证和权限验证。KyuubiServer提供给EngineManager的KyuubiEngine地址。EngineManager从ZK获取当前用户空间可用的Engines,然后统计当前可用Engines的繁忙指标,将相对空闲的Engines返回给KyuubiServer。KyuubiServer将SQL提交给EngineManager建议的Engine执行。(2)用户提交的图为我方用户平台的SQL查询入口。工作区下的用户可以轻松启动KyuubiEngine。为了降低用户使用门槛,只暴露资源相关和排队策略配置。同时,用户还可以配置多个KyuubiEngine实例,保证当前工作空间下SQL执行的高可用。(3)为什么Engine的高可用需要KyuubiEngine的高可用?因为在实际环境中,KyuubiEngine长期运行,Spark的SQL执行过程非常复杂,稳定性随着时间的推移出现问题:启用动态资源策略后抛出事件的bug,导致要释放的资源。大任务耗时较长,可能会阻塞一些小任务的执行。Driver端JVMFullGC时间过长而OOM。SQL不合理导致引擎频繁重启。因此,实现了一些高可用的保障策略:workspace层级隔离Engine异常,避免影响其他用户。观察Engine的可用指示灯,通过busy和probe信息标记当前是否可用。同一个工作空间下的多个Engine实例(Kyuubi的EnginePool机制)提高了整体可用性并提供了基于负载的分配。当发现异常时,会及时自动重启。通过告警机制频繁重启Engine,及时进行人工干预。03基于Kyuubi的Trino和Dorisagent改造Trino和Doris的引入主要解决了OLAP场景下的查询效率问题。Kyuubi在1.1.0版本还没有支持Trino,我们在kyuubiServer端使用Trino-JDBC完成了Trino引擎的适配。Trino-JDBC实现了流迭代器模式,每个nextResult都会触发对Trino引擎的请求。目前社区的Trino-Client实现会一次性拉取所有的结果集,这可能会导致OOM的风险。JDBC方式也用于Doris的适配。由于Doris客户端本身支持MysqlJDBC,所以MySQLJDBC的实现是全拉模式,KyuubiServer端存在OOM风险。目前通过限制Doris查询的超时时间来降低大结果集导致OOM的风险。如果以后要扩展Kyuubi代理支持其他JDBC数据库,一定要慎重。SQLHTTPAPI支持关于HTTPAPI的支持,一共实现了V1和V2版本,与社区相比还是有一些差异。①V1版本简化了用户交互流程,简化了HiveThriftRPC调用流程。用户可以在上层应用程序中通过HTTP请求直接提交SQL,这对一些研发用户来说非常友好。提交SQL根据QueryID,不断轮询得到结果。复用了Thrift后端Service的实现,横向扩展了一层HTTPFrontedService。底层实现与ThriftAPI完全一致。但也存在一些问题:KyuubiService存在Session状态,Step1和Step2必须路由到同一个实例才能获取结果,使用IPHash无法完全解决。这也使得KyuubiServerHTTP服务无法水平扩展和平滑升级。②V2版本为了彻底解决V1的横向扩展问题,KyuubiHTTPServer在V2版本中是完全无状态的,直接通过KyuubiEngine提供HTTPSQLAPI。KyuubiServer仅充当代理。另外两个改进:彻底解决大结果集导致的KyuubiEngineOOM问题,查询类的结果直接持久化到HDFS,无需经过SparkDriver。用户获取结果时不经过KyuubiEngine,直接从HDFS层流中获取结果集。同时无需维护长链接,非常适合ETL场景。SQL表列解析我们在KyuubiServer端做了权限认证,需要获取用户SQL的真实表名,单独开发了一个纯SQL解析模块:支持表列血缘关系和SQL类型的提取,以及支持SparkSQL和Trino语法。解析后的具体格式如图所示,包括类型列、输入输出表和队列。在后续具体的实际场景中,该模块也应用到其业务场景中,如表沿袭审计日志、SQL统计请求分析等安全质量场景,完全复用了我们提取SQL表列的能力。04Kyuubi新增应用小文件合并功能,解决了用户写入场景可能导致的小文件过多的问题。用户一般提交两条SQL:一条是业务处理SQL,一条是合并SQL,通过工作流串联起来,保持不变。启用也很简单,在KyuubiEngine启动阶段和SQL提交阶段打开开关即可。增量取和取结果集的限制主要是用户在JDBC下查询结果集导致的OOM问题,开启了增量模式。但是在某些场景下,可能会出现部分分区的结果过大,导致结果抓取过程阻塞,导致用户体验不好。推荐使用HTTPAPI获取异步结果。对于一些用户的SQL预览数据,如果访问的表很大,限制查询输出的个数,避免不必要的开销,是一个很好的功能。Z-Ordering在我们内部的人像场景中进行了相关测试,Z-Ordering有明显的提升。业务查询时间存储空间查询扫描数据量文件数量在具体应用中,Z-Ordering的排序规则需要根据实际业务表的数据做相应的调整:我们在人像场景中对查询频率高的列进行排序,而且效果明显超过3列最终优化的并不理想。排序列应选择基数大且无偏斜的列。KyuubiEngineZ-Ordering的实现非常巧妙,没有增加额外的列,直接复用了parquet的原生能力,所以一代可以支持多个Engine查询(只要引擎支持读取parquet格式即可)。PlanOnly模式主要用于非SQL执行SQL相关场景,例如:为数据平台提供语法和语义验证服务SQL提交前检查SQL语法和语义兼容性(Spark2.X->Spark3.X升级)PlanOnly模式下面的SQL并不会真正执行,只会输出解析后的LogicalPlan/SparkPlan。目前数据平台单独提供语法语义验证服务,采用KyuubiEngine的PlanOnly模式。这个应用场景也给我们提供了一个新思路:使用KyuubiEngine作为YarnAPP的服务框架,提供其他场景的服务,比如验证服务、血缘抽取服务、SQL预计算服务等。Scala模式ScalaCode模式完全解放了KyuubiEngine的能力,具备了直接通过JDBC提交Scala代码的能力,专门处理一些复杂的逻辑业务。目前我们的应用场景在运维领域做了一些尝试,主要是为了解决我们的运维效率。比如我们想在运行时动态加载用户自定义的jar包,读取Thrift格式的数据。相比之前登录生产集群机器打包代码运行的流程,大大简化了。05未来规划与汇总规划:基于业务场景、SQL规则、执行成本预估,实现多引擎下的自动路由能力。HTTPAPI替代了ThriftAPI提交的ETL作业,异步替代了长连接的方式。总结:Kyuubi是一个非常好的开源实践,已经成为小米内部大数据服务入口的重要基础设施服务。我们非常感谢Kyuubi社区的贡献,他们的贡献加速了我们统一SQL服务的实施。相信未来Kyuubi会成为SQLGateway在大数据场景下的标杆,我们将与您携手共建Kyuubi生态。今天的分享就到这里,谢谢大家。分享客人
