优化和批处理Parquet / JDBC连接

时间:2018-10-04 20:14:30

标签: apache-spark jdbc parquet aws-glue

我正在执行从S3实木复合地板数据到JDBC(Postgres)表的联接操作,使用的是实木复合地板数据中的一列到JDBC表的主键。我需要JDBC表中的一小部分(但仍然是总数很大-总共有成千上万的行),然后我需要智能地对数据进行分区以供执行器使用。

我还是一个整体的数据工程专家,尤其是Spark,因此,请原谅(并假设!)我的无知。我对处理时间的关注比对内存的使用要少。我必须使内存使用量适合Amazon Glue限制。

执行此操作的好方法是什么?

我现有的想法:

从理论上讲,我可以构建如下的SQL查询:

select * from t1 where id = key1 UNION
select * from t1 where id = key2 UNION...

但是,这似乎很愚蠢。这个问题:Selecting multiple rows by ID, is there a faster way than WHERE IN 给我一个想法,将要拉的键写到临时表中,将其与原始表连接起来,然后拉出结果;这似乎是执行上述操作的“正确”方法。但是,这似乎也可能是一个很常见的问题,有一个我还没有找到的现成的解决方案。

也有可能在最小/最大UUID值之间进行提取,但这是我要提取多少行的问题,由于UFAID是AFAIK,随机分布在所有可能的UUID值中,因此我希望获得很多额外的行(在连接过程中将遗漏的行)。仍然,这可能是分割JDBC数据的有用方法。

我还不清楚JDBC数据如何传递给执行者。它可能会(全部)通过驱动程序过程。

因此,尝试将其形式化为问题:

  1. 此用法是否已有配方?
  2. 要实现此目标,我应该查看Spark的哪些功能?
  3. 来自JDBC连接的数据的实际Spark数据流是什么?

2 个答案:

答案 0 :(得分:0)

似乎(到目前为止)最好的方法是将要获取的行ID写入数据库的临时表中,与主表进行联接,然后读出结果(如链接的答案中所述)。

从理论上讲,这在Spark中完全可行;像

// PSUEDOCODE!
df.select("row_id").write.jdbc(<target db>, "ids_to_fetch")
databaseConnection.execute("create table output from (select * from ids_to_fetch join target_table on row_id = id)")
df = df.join(
  spark.read.jdbc(<target db>, "output")
)

这可能是最有效的方法,因为(AFAIK)它将把ID 的写入和的联接表的读取都交给执行者,而不是尝试执行驱动程序中有很多东西。

但是,现在我无法将临时表写入目标数据库,因此我在驱动程序中生成了一系列select where in语句,然后提取这些结果。

答案 1 :(得分:0)

Spark并非旨在对驾驶员执行任何高性能的事情,最好避免这样做。

对于您的情况,我建议先将数据从S3加载到某些DF。保留此数据帧,因为以后需要。

然后,您可以使用 map(row->)。distinct()

的组合为S3的键解析唯一值。

然后,键上方的分区在每个分区中具有合理数量的键,以便对JDBC进行单个查询。您也可以保持结果不变,并执行 count()操作,然后执行 repartition()。例如,单个分区中的项目不超过1000个。

然后使用 mapPartitions 组成一个查询,例如“ SELECT * FROM table WHERE key in”。

然后使用spark flatMap 需要执行实际选择。我不知道自动使用数据帧的方法,因此您可能需要直接使用JDBC来执行选择和映射数据。您可以在工作计算机上初始化spring框架,并使用spring数据扩展名轻松地将数据从DB加载到某些实体列表。

现在,您具有DatasSet,其中包含集群中Postgres的必需数据。您可以通过 toDF()从中创建数据框。此处可能需要对列进行一些其他映射,或者在上一步中将数据映射为 Row 类型。

因此,现在您有2个必需的数据帧,一个保留了来自S3的数据的开头,另一个保留了来自Postgres的数据,您可以使用 Dataframe.join 以标准方式将它们加入。 >

注意:当要重用时,不要忘记使用 .persist()保留数据集和框架。否则,它将每次重复所有步骤以进行数据检索。