使用duckdb查询精度的木质

2021-06-26 03:27:28

TLDR:DuckDB,免费和开源分析数据管理系统,可以直接在镶木地板文件上运行SQL查询,并自动利用木质格式的高级功能。

Apache Parquet是分析的最常见的“大数据”存储格式。在Parquet文件中,数据以柱状压缩二进制格式存储。每个镶木文件都存储单个表。该表将被分成行组,每个行组包含表的行的子集。在行组中,表数据以柱状方式存储。

Parquet格式具有许多属性,使其适用于分析用例:

柱状表示表示可以(有效地)读取各个列。无需始终阅读整个文件!

该文件包含每个行组(最小值/最大值和空值的数量)中的每个列统计信息。如果不需要,这些统计信息允许读者跳过行组。

柱状压缩显着降低了格式的文件大小,从而减少了数据集的存储需求。这通常可以将大数据变为媒体数据。

DuckDB的零依赖性地板读卡器能够在镶木地块文件上直接执行SQL查询,而无需任何导入或分析步骤。由于木质地板的自然柱状格式,这是非常快的!

DuckDB将以流式方式读取地形文件,这意味着您可以对不适合您主内存不适合的大地板文件执行查询。

DuckDB能够自动检测任何给定查询所需的列和行。这允许用户分析更大且更复杂的地形文件,而无需执行手动优化或投资更多硬件。

作为一个添加的奖金,DuckDB能够使用Glob Syntax使用并行处理和多个地块文件的所有这些。

作为简短的预告片,这里是一个代码片段,允许您直接在镶木文件的顶部运行SQL查询。

#安装#39;''从&#39选择count(*); taxi_2019_04.parquet'' 2019-04-15&#39之间的pickup_at;和#39; 2019-04 -20'''')。fetchall())

让我们潜入以前的查询,以更好地了解木质格式的力量,与DuckDB的查询优化器相结合。

在此查询中,我们从我们的镶板文件(pickup_at)中读取了一个列。存储在拼贴文件中的任何其他列可以完全跳过,因为我们不需要它们来回答我们的查询。

此外,只有在2019年4月的第15和2019年4月20日之间存在Pickup_At的行会影响查询结果。可以跳过任何不满足此谓词的行。

我们可以在这里使用镶木地板文件中的统计信息在这里的大量优势。可以跳过任何具有低于2019-04-15的Pickup_at的最大值的行组,或者最小值高于2019-04-20。在某些情况下,允许我们跳过整个文件。

为了说明这些自动优化的有效性,我们将使用Pandas和DuckDB在地板文件的顶部运行许多查询。

在这些查询中,我们使用存储为镶木地板文件的臭名昭着的纽约出租车数据集的一部分,特别是2019年4月和2019年6月的数据。这些文件是加利福尼亚州。 360 MB的大小在一起,每个含有约2100万行的18行。将三个文件放入出租车/文件夹中。

此示例可在此处作为谷歌Colab的互动笔记本。这里报告的时间来自这种环境以获得可重复性。

首先,我们在数据集中查看一些行。出租车/文件夹中有三个镶木地板文件。 DuckDB支持Globbing语法,允许它同时查询所有三个文件。

尽管查询从三个(相当大的)拼贴文件中选择所有列,但查询立即完成。这是因为DuckdB以流式方式处理拼影文件,并且在前几行被读取后将停止读取地板文件,因为全部需要满足查询。

如果我们在熊猫尝试这样做,我们意识到它并不是那么简单,因为熊猫不能在一个呼叫中读取多个镶木地板文件。我们首先必须使用pandas.concat将三个镶木档案连接在一起:

PANDAS显着更长的时间来完成此查询。这是因为Pandas不仅需要整体读取三个拼影文件中的每一个,它必须将这三个单独的熊猫Dataframe串联在一起。

我们可以通过从三个较小的部分创建单个大块地板文件来解决串联问题。我们可以使用PyArrow库来实现这一目标,它有支持读取多个镶嵌文件并将它们流入单个大文件。请注意,Pyarrow Parquet Reader是熊猫内部使用的非常相同的木条读卡器。

我们可以看到熊猫比以前更好,因为避免了级联。但是,整个文件仍然需要读入内存,这取得了大量的时间和内存。

对于DuckdB,它并不重要,您需要在查询中读取多少个镶木文件。

现在假设我们想弄清楚我们的数据集中有多少行。我们可以使用以下代码执行此操作:

DuckDB非常快地完成查询,因为它自动识别需要从地块文件中读取的内容,并最大限度地减少所需的读取。 Pandas必须再次读取整个文件,这导致它将其与以前查询相同的时间。

对于此查询,我们可以通过手动优化来改善Pandas的时间。为了获得计数,我们只需要文件中的单列。通过在read_parquet命令中手动指定要读取的单个列,我们可以获得相同的结果,但更快。

虽然这更快,但由于整个Vendor_ID列必须将其作为Pandas列读入内存,因此仍然需要超过一秒钟仅计算行数。

通常使用某种过滤谓词仅查看数据集的有趣部分。例如,想象一下我们想知道2019年6月30日之后发生了多少出租车发生。我们可以使用DuckDB中的以下查询来执行以下操作:

但是,它再次将整个文件读入内存,导致此查询需要7.5s。使用手动投影推动,我们可以将此降至0.9s。仍然明显高于DuckdB。

然而,Pyarrow Parquet Reader还允许我们对扫描进行滤波器推动。一旦我们添加了这一点,我们最终会更具竞争力的70ms来完成查询。

这表明这里的结果不是由于DuckdB的木质读者比Pyarrow Parquet Reader更快。 DuckDB在这些查询上更好地表现更好的原因是其优化器从SQL查询自动提取所有必需的列和过滤器,然后在镶木地板读卡器中自动使用,无需手动努力。

有趣的是,Pyarrow Parquet Reader和DuckdB都比在物化DataFrame上在Pandas上本身进行此操作的速度快得多。

#将整个镶木地板文件读入Pandas DF = Pandas。 read_parquet(' alltaxi.parquet')#在熊猫#注意34; pickup_at>' 2019-06-30'"))))

最后让我们看看更复杂的聚合。假设我们想计算每次乘客的游乐设施数量。使用DuckDB和SQL,它看起来像这样:

对于SQL-Averse并且作为未来博客文章的预告片,DuckDB还具有“关系API”,其允许更具Python-esque的查询声明。以下是与上述SQL查询相当,它提供了完全相同的结果和性能:

现在作为比较,让我们以先前的方式在熊猫中运行相同的查询。

#天真的熊猫。 read_parquet(' alltaxi.parquet')。 groupby(' passenting_count')。 agg({' passenting_count':' count'})#投影推平熊猫。 read_parquet(' alltaxi.parquet',columns = [' passenger_count'])。 groupby(' passenting_count')。 agg({' passenting_count':' count'})#本地(Parquet文件预加载到内存中)df。 groupby(' passenting_count')。 agg({' passenting_count':' count'})

我们可以看到DuckDB在所有三种情况下都比熊猫更快,而无需执行任何手动优化,而无需将拼影文件加载到内存中。

DuckdB可以在镶木地板文件的顶部有效地运行查询,而无需初始加载阶段。系统将自动利用所有镶木地板的高级功能来加快查询执行。

DuckDB是一个免费和开源的数据库管理系统(MIT许可)。 它旨在成为分析的SQLite,并提供一个快速高效的数据库系统,具有零外部依赖项。 它不仅适用于Python,还可用于C / C ++,R,Java等。 ←返回新闻档案