我看很多企业都有选择深圳滴普智能科技有限公司科技的湖仓一体,大家知道为什么吗?

Xin)的采访,讲述了Databricks公司创建的历史背景、发展过程中对战略的坚持和战术的调整、未来的愿景等,感觉挺有意思,推荐大家听听。

Databricks是由UC Berkeley实验室的成员创立的公司,也是最成功的开源项目之一Spark背后的商业公司,经过10年左右的发展,公司All in Cloud的初衷没有变过,但其最早的定位是不做data warehouse,专注于Maching Learning类型的workload,利用数据湖的海量存储作为统一的数据底座。但随着业务的不断发展,公司发现越来越多的客户对数据有着多样化的处理需求,其中很大的一类便是BI分析,此外还有streaming processing等,这些仅仅依靠data lake自身的功能和性能是无法满足的,因此近些年提出了”Lakehouse“这个概念,将数据湖和数据仓库统一到一个平台中,即目前流行的湖仓一体。

为了能够提供数仓的能力,databricks需要提供数仓所具有的各类管理功能,事务型写入和读取的能力,以及足够好的性能,其实可以对标下snowflake做的事情,也就明确了databricks需要补齐的组件:最核心的就是对于存储于data lake中的数据对象的管理能力,包括其元数据、统计信息等,以及ACID的语义。这些功能snowflake是通过cloud

lake又可以构建一系列类似数仓的上层特性,因此可以说它是lakehouse的核心组件之一。

本篇文章是对的解读,感兴趣的同学可以去读读原文。

  1. 超高扩展性,可以认为是近乎无限的存储容量
  2. 超高持久性,写入后不用担心数据的丢失
  3. 读取的延迟大,单链接吞吐量不高

这种key-value的组织形式类似filesystem,但并没有文件系统这么丰富的操作接口。而是提供了例如LIST这样的metadata API,用来快速获取data object列表,列举的方式是按照字典序获取比给定key(filepath)更大的object集合,也就相当于读取某个特定"目录"下的所有文件+子目录。但这个操作效率很低(S3一个LIST

consistency的保证,也就是说,对一个key-value对象,写入后不一定立即可见,有些系统甚至无法做到read-after-write,此外单个object的写入是原子的,但多object之间不提供原子写能力,metadata也是如此,LIST不一定看到最新状态。

从性能角度,每次读操作的基础延迟是5-10ms,这就意味着需要做大块的sequential IO(xxMB)来均摊掉这部分latency,此外由于单个read操作吞吐低(50-100M/s),需要在上层做充分并行来提升throughput。LIST操作也需要并行来加速,但如果data object非常多,即使并行了延迟也仍然很大。写操作是原子的,但这里会有读性能和写延迟的trade-off,如果要求写入得快,可能产生文件会比较碎比较小导致不利于读,反之则写延迟会变大。

提供事务型的读写能力并保证性能ok,必然要克服以上问题,包括:

最简单的方式就是没有单独的元数据管理层,只是原生的一堆data objects集合,data objects按照类似文件目录的方式来组织,例如如果业务侧有分区,可以把不同分区组织为不同子目录,但由于上面提到的各类一致性、性能缺陷,用户会遇到类似partial update,corrupt state,没有任何管理接口,性能糟糕等问题。

这也是Snowflake/Hive等系统采用的方案,把metadata,statstics组织在一个单独的,高可用的,具备事务能力的存储系统中,作为底层数据视图的"source of truth",并利用其ACID的能力提供对底层存储的事务支持,包括并发控制、多版本管理等。这样data lake就成了一个dummy的存储系统而已,策略完全在第三方组件里。这是一个很不错也很流行的方案,但paper认为它存在以下问题:

  1. 所有I/O操作都要以第三方组件作为入口点,例如Snowflake cloud service,这使得集成变得更复杂,也降低了性能,databricks更倾向于直接访问底层存储。
  2. 存在供应商绑定问题,不具备开放性。
  3. 额外组件意味着更高的运维成本、失效风险。

Iceberg也采用了同样的机制,但他们没有像Databricks一样提供更全面的上层能力,后面会介绍。

本质上,delta lake是通过transaction log信息,结合一套定制的数据组织方式 + 访问协议,来实现这种transactional的语义,理论上可以完全没有第三方组件的依赖,是一套纯粹的软件定义机制。

每个table都有对应的_delta_log目录,各自维护自己的log,它被组织为一系列的JSON文件,序号单调递增。每个log record object由一个事务原子写入,记录了该事务的操作内容,log record的顺序就是事务的提交顺序,图中可以看到,这个序号是单调连续递增的。

每个log record由一系列的action组成,包括如下一些类型,一个record中的所有action原子性生效。

remove action同时记录一个timestamp,表示对应file实际需要被删除的时间点,在那之前,data file会一直保持所以删除不会影响已经执行中的并发读操作。

记录访问协议的版本,和客户端兼容性相关

记录当前log record由谁写入,可以用来做审计

record完成提交,其他trx发现失败只能重试,写下一个log record。

  1. 执行LIST操作获取log ckpt之后所有log records(可能拿不到所有,也可能不连续),使用能读到的最大log record ID,去读取所有之前的record + ckpt文件,如果不存在要waiting直到读到
  2. 并行读取所有需要的data object,如果读不到也要重试

可以看到整个过程都容忍了eventual consistency,此外由于不一定在第2步读到最新的log record,这只能保证一个快照读。

  1. 利用读事务的1-2两步,获取"最近"的log record ID r,并读取所需数据
  2. 写入新data object files到目标数组子目录中(根据partition),可以并行写入来利用带宽
  3. 使用”新“的版本r+1,写入新的log record来完成提交,如果发现r+1已存在log record而失败(put-if-absent),则递增版本重试提交,直到成功。
  4. 当发现之前已累加了10个log records时,则生成新的ckpt文件,注意这不在提交路径中,是个独立的优化操作,即使失败了也不影响正确性。

注意这里有个隔离级别的问题,如果读取的版本r和最终写入的版本r'并不连续,则无法保证serialization,只能是snapshot isolation。如果想做到serialization,需要做到两点:

  1. 读取时,需要读真正的最新版本数据,这可以通过一个dummy write来实现
  2. 事务提交失败重试时,需要整体重试,直到r读 -> r+1写入成功为止。

前面提到了"put-if-absent"的能力,这是保证事务语义的重要基础,一些对象存储如Google Cloud Storage和Azure Blob Storage天然支持,而HDFS可以通过原子的rename操作模拟支持。AWS S3没有提供这种能力,因此需要一个额外的协调组件,用来协调不同事务的提交顺序(log record顺序),由于只需要做协调因此可以很轻量,例如Spark

通过上面的描述,大家应该也能预料到这种并发控制方式的性能如何,首先读存在base latency,写操作延迟一般在10到几百ms,考虑到commit时的冲突重试就以为着重新写,一旦并发稍高各个事务就会延迟严重,导致吞吐量很低( << 100/s),因此合适的场景是少并发 + 大事务,尽量把变更batch起来,或者利用额外coordinator组件协调提交顺序来加速commit。

由于事务型的table storage语义,Delta lake可以构建出很多类似传统DBMS的管理性功能,并提升自身的查询性能,从而提供类数仓的能力。

有了snapshot读的能力,可以利用老版本的数据去rollback掉一些错误注入的新数据,或者在ML这样的workload下,可能需要反复针对同一版本数据做多个模型训练然后横向比较。delta lake的API提供了由用户指定log record ID的机制来读取特定版本的数据,使用类似"AS OF timestamp" 或 "VERSION AS OF commit_id"的SQL语法来完成。同时使用如下merge操作来用老版本修复不正确的数据:

很多data warehousing场景会有频繁数据更新的场景,如更新错误数据,删除某一类特定数据,对流式数据的derived table做持续更新等,事务性的更新能力通过add/remove操作来完成。

常用的流处理模式是利用类似Kafka这样的message queue来完成数据的分发,这自然有数据重复和额外组件维护问题。可以使用Delta lake log来实现同样的message queue功能,这需要以下3个能力:

为了保证流入数据快速注入,需要写很多小的data object,但前面已讨论了写延迟和读性能之间的tradeoff,因此需要有后台compaction机制,已事务的方式,将多个小object合并为大的文件,同时对新文件要设置dataChange flag为false,使已经发生的读操作跳过大文件,避免错误结果。

这样从注入的new -> old顺序上,可以保证对新数据快速写入,对老数据快速读取。

但这里要注意的是,由于object storage自身延迟的特点,是无法做到类似Kafka这样ms级延迟的,通常pipeline的两端,延迟在秒级

对于selective query来说,data layout对性能有很大影响,由于有这种事务更新能力,可以在做后台layout优化的同时不影响前台业务,例如:

warehouse一样,为了提升查询性能,Databricks也实现了向量化的执行引擎和MPP分布式执行,对列式数据做更高效的计算。

Data lake中的数据集会时不时有schema变更的情况,不过由于事务的顺序写入机制,可以利用之前的metaData log record来对后续的写入数据做schema的检查,保证不会写入错误格式的数据。此外schema change如果涉及之前的数据变更,也是可以利用事务的原子性来完成所有修改的,而且某些变更(e.g 加列)可以不修改老数据。

由于数据湖中可存储数据模型的多样化,湖仓一体的Delta lake可以被各类场景所使用,包括流处理、报表、数据科学、ML、图分析等。

Lakehouse这种系统可以简化传统大数据平台中,复杂多样的组件、数据多份存储、非实时数据可见性等问题,例如在传统的lambda架构中,需要Kafka + Flink做streaming processing,完成ETL的数据导入并做一些预处理计算存储在derived table中,数据将data lake作为长期存储区,再使用额外的ETL pipeline注入到data warehouse中做BI分析。现在有了Delta lake的事务能力,结合前面提到的streaming I/O + SSD caching,可以避免大多数冗余组件,保证在一份数据下,利用不同计算集群应对不同workload,完成原来需要多个系统才能做的事情,类似下图:

Delta lake秉承了Riselab一贯开源和开放的原则,因此其设计上也最小化了外部依赖,利用通用性最大化的方便了第三方framework或组件的接入,其项目地址在 .

但其目前的设计和实现还是比较trivial的,存在不少问题:

  1. 现有机制在并发和延迟上表现都不理想,这点也遭到了,例如可以支持并发查询用户数过低,流处理的延迟较长等,如果要提升并发能力,还是要引入第三方的coordinator做事务排序。

尽管如此,Databricks提出的湖仓一体引领了新的大数据技术发展的方向,各大云厂商和很多第三方数据厂商都在提供类似形态的产品。最有竞争力的目前当属IPO后大火的snowflake,虽然它从云数仓起家,但从其近年的技术演进和投资收购策略来看,最终还是要向所谓的data cloud发展,形成统一的数据分析平台,和Databricks跑在了完全重叠的赛道,这也是两边去年在tpc-ds benchmark上数次激烈交锋的根本原因之一。

我要回帖

更多关于 Sap云和本地部署哪个发展趋势好的 的文章

 

随机推荐