我正在pyspark中的(x,y)点的RDD上实现范围查询。我将xy空间划分为16 * 16网格(256个单元格),并将RDD中的每个点分配给其中一个单元格。
gridMappedRDD是PairRDD:(cell_id, Point object)
我使用以下命令将此RDD分区为256个分区:
gridMappedRDD.partitionBy(256)
范围查询是一个矩形框。我有一个Grid对象的方法,它可以返回与查询范围重叠的单元格id列表。因此,我使用它作为过滤器来修剪不相关的细胞:
filteredRDD = gridMappedRDD.filter(lambda x: x[0] in candidateCells)
但问题是,在运行查询然后收集结果时,将评估所有256个分区;为每个分区创建一个任务。
为了避免这个问题,我尝试将filteredRDD合并到candidateCell列表的长度,我希望这可以解决问题。
filteredRDD.coalesce(len(candidateCells))
实际上,生成的RDD具有len(candidateCells)
个分区,但分区与gridMappedRDD
不同。
正如coalesce文档中所述,shuffle
参数为False,并且不应在分区之间执行shuffle,但我可以看到(在glom()的帮助下)情况并非如此。
例如,在coalesce(4)
candidateCells=[62, 63, 78, 79]
之后,分区是这样的:
[[(62, P), (62, P) .... , (63, P)],
[(78, P), (78, P) .... , (79, P)],
[], []
]
实际上,通过合并,我有一个随机读取,它等于我的每个任务的整个数据集的大小,这需要很长的时间。我需要的是一个RDD,只有与candidateCells中的单元格相关的分区,没有任何改组。 所以,我的问题是,是否有可能只过滤一些分区而不进行重新洗牌?对于上面的示例,我的filteredRDD将具有4个分区,其具有与原始RDD的62,63,78,79个分区完全相同的数据。这样做,可以将查询定向到仅影响分区。
答案 0 :(得分:3)
你在这里做了一些不正确的假设:
coalesce
无关(此处coalesce
也不常用)。它是由partitionBy
引起的。根据定义进行分区需要随机播放。filter
。 Spark对你使用的功能一无所知(它是一个黑盒子)。你能做什么:
如果生成的子集是小的重新分区,则为每个密钥应用lookup
:
from itertools import chain
partitionedRDD = gridMappedRDD.partitionBy(256)
chain.from_iterable(
((c, x) for x in partitionedRDD.lookup(c))
for c in candidateCells
)
如果数据很大,您可以尝试跳过扫描分区(赢得的任务数量不会改变,但某些任务可能会被短路):
candidatePartitions = [
partitionedRDD.partitioner.partitionFunc(c) for c in candidateCells
]
partitionedRDD.mapPartitionsWithIndex(
lambda i, xs: (x for x in xs if x[0] in candidateCells) if i in candidatePartitions else []
)
这两种方法只有在执行多次"查找"时才有意义。如果是一次性操作,最好执行线性滤波器: