开发者学堂课程【Databricks数据洞察公开课:Delta Lake 数据湖基础介绍(商业版)】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/1058/detail/15563
Delta Lake 数据湖基础介绍(商业版)
内容介绍
一、Lake house 搜索引擎的介绍
二、技术优化介绍
三、Delta Clones
一、 Lake house 搜索引擎的介绍
从早期的数据仓库到今天的 Lake house
Lake house 可以同时存储结构化、半结构化和非结构化的数据,并且同时支持流分析、BI数据科学和机器学习的场景。
在 Data ware house 架构下,数据仓库可以完全控制数据的存储和查询,因此可以同时设计查询系统以及适合查询系统的数据存储结构,来达到优越的查询性能。而在Lake house 架构下,数据是用开放存储结构来存储的,例如 Parquet 格式,使用开放存储结构来存储,因此很多不同的系统,就可以很方便的直接访问这些数据。但是开放的存储格式不一定适合执行查询操作,而查询系统也不一定有足够的统计数据来产生高效的查询计划。
二、技术优化介绍
Lake house 怎么用开放的存储格式来达到优越的查询性能呢?为了解决问题,database
Lake house 重新设计了一个新的查询系统,新的查询系统采用以下的技术,来在开放的存储格式下实现优越的查询性能。技术包括使用把热数据放在高速缓存里,建立辅助的数据结构,例如收集统计数据和建立索引,优化数据布局来减少 I/O 以及使用动态文件剪枝来减少 I/O 。
下面是每一个技术优化的介绍
优化1 采用高速缓存
大部分的工作负载一般都会集中的访问某一部分的热门数据,数据仓库经常使用 SSD 和内存作为缓存,来加速热数据的查询。而在 Lake house 上,如果可以用 Delta Lake 一样很好地管理源数据,来正确的维护缓存中的数据,也可以采用同样的缓存技术来加快数据的查询。例如在Databricks 可以用 SSD 作为缓存,把数据的读取速度提高三倍以上,可以采用 delta engine 作为缓存,把数据速度提高七倍以上。
优化2 建立辅助的数据结构
即使数据是用 Parquet 格式存储的,也可以建立很多额外的数据结构来加快查询,并且对额外的数据结构,提供事务性的维护。例如对于每一个Parquet 文件,可以收集存储的数据的每一列的最小值和最大值。并且当数据进行发生变化的时候,同时事务性的维护统计上的最小值和最大值,确保正确性。
当有查询进来的时候,就可以用统计数据跳过一些没有必要的文件。假设查询只需要年等于2020,然后 UID 等于24,000 的数据的话,利用最大值和最小值的统计数据,就可以知道相关的数据只能存在第三个文件里面。就可以跳过第一和第二个文件的读取。
可以建立索引,譬如 B 数来加快数据的检索。假设查询只需要type 等于某一个固定的值的数据,可以利用建立在 type 列上的索引,直接的跳到对应的数据上,从而避免读取很多没有必要的数据。可以给每一个文件建立一个Bloom filter , bloom filter 可以快速的判断表文件中是否包含要查询的数据,如果不包含就及时的跳过文件,从而减少扫描的数据量,提升查询性能。其源理是对于存在在文件里面的数据,对于每一个记录,使用一个或者多个哈希表计算其哈希值。 Bloom filter 里面的位起始的默认值都是0。
当有一个哈希值映射在对应的位上的时候,就可以把位设为1。当有查询进来的时候,譬如需要看type 等于一个具体值的数据的时候,可以应用同样的哈希函数去计算给定值的哈希值。
然后看对应的位。如果对应的位有0,那么表示此数据肯定不在文件里面,可以跳过。
假设对应的位全部是1,表示数据有可能存在文件里面,要读取这个文件来确定。当对应的位全部都是 1 的时候,数据也有可能不在这个文件里面,其实就是假阳性。
通过控制使用哈希函数的个数,以及 Bloom filter 的大小来控制这个假阳性率。
优化3 数据布局
经常的 merge 、 update 和 insert 操作可能会产生很多的小文件,太多的小文件会降低读性能,因为每次 I/O 能够连续读取的数据变小了,太多的小文件也会提高源数据操作的开销。在 Lake house 里面,使用了不同的技术来减少小文件的产生。例如可以使用额外的 executors 来处理每个 partition 。在开源版的 SParquet 里面,每个 executor 向 partition 中写入数据的时候,都会创立一个新的表文件进行写入。
在例子里面有三个 executor 对 partition 1 写入了数据,那么 SParquet 就创建了三个不同的文件,最终会导致一个 partition 中会产生很多的小文件。
然后 Databricks 对 delta 表的写入过程进行了优化。对于每一个 partition 都使用一个专门的 executor 来合并其他 executors 对 partition 的写入,从而避免了小文件的产生。
除此以外,data place 提供了小文件自动合并功能。在每次在 delta 表中写入数据之后,会检查 delta 表中的表文件数量。如果 delta 表中的小文件达到一定的值,例如有很多的文件,其 size 是小于 128 兆,代表是一个小文件。假设小文件的数量太多,过了就会执行一次小文件合并,将delta 表中的小文件合并为一个大文件。除了自动合并以外,还提供了一个 optimize 命令,使用户可以手动地合并小文件。优化表结构,使得表文件的结构更加的紧凑。
查询的运行时间很大程度上取决于要读取数据的量。即使是用 Parquet 的存储格式。也可以优化数据布局来减少运行时间。一个方法是把存储的数据进行排序。例如第一个文件里面只存储 UID 从 0 到 1000 的数据,第二个文件只存处从 1001 到 2000 的数据等等。查询的时候,如果要查询的 UID 范围比较窄,就可以只读取其中少部分的文件。而跳过大部分的不相关的文件。
以使用 Z-ordering 来对包含多个纬度的查询进行优化。查询可能会多种多样,有一些查询需要看 clones 1 在某一个范围的数据,有一些查询需要看 clones 2 在某个范围的数据,还有一些需要看 clones 3 或者 clones 4 在某个范围的数据。
那如果仅仅对 clones 1 进行排序,那么排序能够很好的帮助 clones 1 进行剪枝,却不可以很好的帮助 clones 2 、clones 3 或者 clones 4 去跳过不相关的文件。而使用Z-ordering ,可以把多个纬度上,例如 clones 1、2、3、4 四个纬度上值比较接近的数据都放在一起。利用Z-ordering 的布局,就可以在不同的维度上,所有的 clones 1、2、3、4 四个维度上进行剪枝,来减少不必要的文件读取。
优化4 优化动态文件剪枝
假设一个查询,需要把 store sales 和 item 两个表连起来,在两者的 item SK 值相等的时候,并且只关心 item ID 等于某个固定值的 items ,那 store sales 表是一个非常大的表,里面有大约 86 亿个记录。而 item 表是一个小表,里面只有大约 36 万个记录。在查询里面, item ID 等于固定值的。
items 其实只有 3 行,在扫描了整个 store sales 表的 86 亿行数据,并且和 item 表进行 join 了之后,只产生了大约 48,000 行的数据。用户需要扫描整个的 source 的表。
对于查询,如果使用动态文件剪枝的话,就可以先扫描item 表,找到里面满足 item ID 等于给定值的三行数据。
然后再找到三行数据里面对应的 item SK 的值。在查询里面,三行数据也只有三个 item SK 的值分别是40、41和42。
在 store sales 的表里面能够满足 join condition 的数据,两者的 item SK , SK 值也只能是40、41和42。可以利用三个 item SK的值以及相关文件的最小值和最大值或者 bloom filter 来进行剪枝,然后排除大部分的不相关的文件。
在实验中,动态文件简直可以把需要扫描的 store sales 表的记录从 86 亿减少到 6600 万,减少了大约99%。
把动态文件剪枝的技术应用到 TPC DS 的查询上,很多的查询的性能得到了很大的提升。图表里面包含了性能提升最多的 10 个查询,查询速度得到了大约 4.5 倍到大约 8 倍的提升。
进行优化后,Lake house 里面大部分的读取都是从高速缓存里进行的,并且使用了各种各样额外数据结构或者是数据布局,来减少了对非缓存数据读取的 I/O ,那么 Lake house 的引擎可以提供跟数据仓库类似的查询性能。
在图表里面,delta engine 的查询性能跟第一个数据仓库类似,并且比第二个和第三个数据仓库快了很多。
三、Delta Clones
下面介绍的是 Lake house 里面一个非常有用的技术 delta clones ,可以对很大的数据集进行高效的拷贝,来支持测试分享和机器学习的不同需求。
什么是克隆呢?克隆也叫拷贝。顾名思义,克隆就是源始数据在一个给定的时间的复制品。
和源始数据有一样的 metadata ,一样的源数据,譬如有一样的 schema 、 一样的限制、一样的列、一样的描述、一样的统计数据和一样的分块。
其中有两种方式的拷贝,一个叫做 shallow clones就是浅拷贝,一个叫 deep clones 就是深拷贝。
在 SQL 语句里面,可以运行一个 create table 的语句进行拷贝。在 Python 和 Scala 里面,可以运行一个 delta table 的语句进行拷贝。默认的拷贝是深拷贝。
深拷贝会把源数据和数据文件都复制一份声拷贝会产生一个全新的独立的表,有自己分开的单独的 history ,跟原始的数据不一样。而且对原来数据的修款修改并不会影响到拷贝。
进行浅拷贝的语句跟深拷贝类似,在 SQL 里面,需要加上 shallow 关键词,进行一个浅拷贝。而在 Python 和 Scala 里面,需要加上 isShallow 等于 true 关键字来表示需要进行的四个浅拷贝。
浅拷贝只会复制源数据,而不会复制数据文件,因此不是自含的,就不是 self contain ,如果原始数据被删除了,那么浅拷贝很有可能就不能再使用了。
浅拷贝也不会拷贝 transactions 和 copy into 相关的源文件。原来跑在原始数据上的 ETL 应用不可以直接的转移到浅拷贝上继续进行,如果要在浅拷贝上运行,就必须是从头开始,不能够从之前断掉的地方开始。
浅拷贝的应用场景有非常的多,可以应用在数据的存档上、短期的实验、数据的分享和灾难的恢复上,短期的实验一般使用浅拷贝就足够了。
而其他的三个场景则需要使用深度拷贝来把数据文件也拷贝一份。阿里云和 Databricks 联合打造的数据洞察产品,使用了提到的各种优化技术,是一个基于SPA 的云上全托管大数据分析和 AI 平台,可以为用户提供数据分析、数据工程、数据科学和人工智能等方面的服务,来构建一个一体化的 Lake house 架构。