为了限制大RDD

时间:2016-08-02 00:26:30

标签: python hadoop apache-spark pyspark distributed-computing

我正在阅读许多图像,我想在它们的一小部分图像上进行开发。因此,我试图了解如何实现这一目标:

In [1]: d = sqlContext.read.parquet('foo')
In [2]: d.map(lambda x: x.photo_id).first()
Out[2]: u'28605'

In [3]: d.limit(1).map(lambda x: x.photo_id)
Out[3]: PythonRDD[31] at RDD at PythonRDD.scala:43

In [4]: d.limit(1).map(lambda x: x.photo_id).first()
// still running...

..那么发生了什么?我希望limit()的运行速度比[2]的速度快得多,但情况并非如此 *

下面我将描述我的理解,请纠正我,因为显然我错过了一些东西:

  1. d是对的RDD(我从模式中知道)我说 使用地图功能:

    i)取每一对(名为x并返回photo_id属性)。

    ii)这将产生一个新的(匿名)RDD,我们正在应用first()方法,我不知道它是如何工作的 $ ,但是应该给我匿名RDD的第一个元素。

  2. [3]中,我们将d RDD限制为1,这意味着尽管dOut [3] 许多元素,只使用1并将map函数应用于那个 仅元素。 [4]应该是映射创建的RDD。

  3. [3]中,我希望遵循limit()的逻辑,只打印有限RDD的唯一元素......
  4. 正如预期的那样,在查看显示器后,[4]似乎处理整个数据集,而其他数据集不是,所以我似乎没有正确使用tiny_d = d.limit(1).map(lambda x: x.photo_id) tiny_d.map(lambda x: x.photo_id).first() ,或者那不是我要找的东西:

    enter image description here

    编辑:

    PipelinedRDD

    第一个会提供<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApplication1" mc:Ignorable="d" Title="MainWindow" Height="500" Width="983.334"> <Button x:Name="Button1" Content="Database" HorizontalAlignment="Left" Margin="10,427,0,0" VerticalAlignment="Top" Width="99" Click="Button1_Click"/> </Window> ,如here所述,它实际上不会执行任何操作,只是转换。

    但是,第二行也将处理整个数据集(事实上,现在的任务数量与以前一样多,加上一个!)。

    * [2]立即执行,而[4]仍在运行且&gt; 3h已经过去了..

    $ 由于名称,我在文档中找不到它。

1 个答案:

答案 0 :(得分:4)

根据您的代码,这里是Spark 2.0上更简单的测试用例

case class my (x: Int)
val rdd = sc.parallelize(0.until(10000), 1000).map { x => my(x) }
val df1 = spark.createDataFrame(rdd)
val df2 = df1.limit(1)
df1.map { r => r.getAs[Int](0) }.first
df2.map { r => r.getAs[Int](0) }.first // Much slower than the previous line

实际上,Dataset.first相当于Dataset.limit(1).collect,所以检查这两种情况的实际计划:

scala> df1.map { r => r.getAs[Int](0) }.limit(1).explain
== Physical Plan ==
CollectLimit 1
+- *SerializeFromObject [input[0, int, true] AS value#124]
   +- *MapElements <function1>, obj#123: int
      +- *DeserializeToObject createexternalrow(x#74, StructField(x,IntegerType,false)), obj#122: org.apache.spark.sql.Row
         +- Scan ExistingRDD[x#74]

scala> df2.map { r => r.getAs[Int](0) }.limit(1).explain
== Physical Plan ==
CollectLimit 1
+- *SerializeFromObject [input[0, int, true] AS value#131]
   +- *MapElements <function1>, obj#130: int
      +- *DeserializeToObject createexternalrow(x#74, StructField(x,IntegerType,false)), obj#129: org.apache.spark.sql.Row
         +- *GlobalLimit 1
            +- Exchange SinglePartition
               +- *LocalLimit 1
                  +- Scan ExistingRDD[x#74]

对于第一种情况,它与CollectLimitExec物理运算符中的优化有关。也就是说,它将首先获取第一个分区以获得限制行数,在这种情况下为1,如果不满足,则获取更多分区,直到达到所需的限制。因此,通常,如果第一个分区不为空,则仅计算并获取第一个分区。甚至不会计算其他分区。

但是,在第二种情况下,CollectLimitExec中的优化没有帮助,因为先前的限制操作涉及随机操作。将计算所有分区,并在每个分区上运行LocalLimit(1)以获得1行,然后将所有分区混洗到单个分区中。 CollectLimitExec将从生成的单个分区中获取1行。