使用LIMIT两次评估Spark SQL查询时获得相同的结果

时间:2018-01-22 14:19:17

标签: apache-spark pyspark apache-spark-sql pyspark-sql

我最近开始使用pyspark,我遇到了一些我想要更好理解的行为,并避免使用。

请考虑以下代码:

query1 = "SELECT * FROM A where X >= 1000000 and X < 1001000 LIMIT 50"
s1 = spark.sql(query1)
X_vals = s1.select('X').rdd.flatMap(lambda x: x).collect()

query2 = "SELECT * FROM B" + " where Y in " + '('  + ','.join([str(x) for x in X_vals]) + ')'
s2 = spark.sql(query2)

s1.write.mode('overwrite').option("header", True).option("sep",'\t').csv('test/A.csv')
s2.write.mode('overwrite').option("header", True).option("sep",'\t').csv('test/B.csv')

A开始,我从一个范围中获取50条记录的样本,并将X的值存储在X_vals中。然后,我从表Y中获取相同的记录(X_vals中的B)。

稍后,我将这两个表写入csv个文件。在生成的csv个文件中,X中的A不再与Y中的B匹配。

我认为这是可解释的行为,是由懒惰评估造成的; collect()语句中选择的记录与.csv语句中的记录不同。但是我对Spark的理解还不足以解释为什么会发生这种情况。

因此;为什么会发生这种情况,有没有办法强制查询两次返回相同的结果(不加入表)?

谢谢,

弗洛里安

1 个答案:

答案 0 :(得分:3)

问题是LIMIT的实施。它通过将记录改组到单个分区来实现(您可以在Towards limiting the big RDD的优秀答案中找到详细解释)。

同时,Spark遵循SQL标准规则 - 如果没有明确的顺序,那么优化器可以选择任意记录。

val df = spark.range(1000)

df.where($"id".between(100, 200)).limit(10).explain
== Physical Plan ==
CollectLimit 10
+- *LocalLimit 10
   +- *Filter ((id#16L >= 100) && (id#16L <= 200))
      +- *Range (0, 1000, step=1, splits=4)

为了获得确定性(在某种程度上,AFAIK关系得到非确定性解决),请使用orderBy子句将CollectLimit转换为TakeOrderedAndProject

df.where($"id".between(100, 200)).orderBy("id").limit(10).explain
== Physical Plan ==
TakeOrderedAndProject(limit=10, orderBy=[id#16L ASC NULLS FIRST], output=[id#16L])
+- *Filter ((id#16L >= 100) && (id#16L <= 200))
   +- *Range (0, 1000, step=1, splits=4)