很明显,为了更好地分配小型查找数据以使用广播变量。
假设我们在纱线客户端模式下从主节点运行pySpark代码(spark submit)。因此,应始终在主节点上创建应用程序驱动程序。我们从主节点上的本地路径读取文件。
with open('/tmp/myfile.txt', 'r') as f:
lookup = {}
for line in f.readlines():
line = parse(line) # Method parse uses re and return dict
lookup[line['name']] = line['age']
然后我们创建广播变量并使用它:
lookupBC = sc.broadcast(lookup)
output = sc.textFile('/path/to/hdfs/')\
.map(lambda e: (lookupBC.value.get(e, e), 1))\
.collect()
在我们的例子中,这个bc var是在驱动程序(主节点)上创建的,并且spark会在集群中的所有数据节点之间复制此var,其中创建执行程序,将其保留在这些节点的内存中。 因此,文件将被读取一次,然后分发给执行者。
如果我们使用 addFile 选项会发生什么?
sc.addFile('/tmp/myfile.txt')
with open(SparkFiles.get('/tmp/myfile.txt')) as f:
lookup = {}
for line in f.readlines():
line = parse(line) # Method parse uses re and return dict
lookup[line['name']] = line['age']
output = sc.textFile('/path/to/hdfs/')\
.map(lambda e: (lookup.get(e, e), 1))\
.collect()
Spark会将文件'/tmp/myfile.txt'
复制到每个节点,其中将创建执行程序。然后:
在执行程序日志中,我看到有关bc变量的信息,但我的代码中没有使用任何内容:
18/03/21 15:36:27 INFO util.Utils: Fetching spark://172.25.235.201:36478/files/myfile.txt to /data/disk01/yarn/nm/usercache/testuser/appcache/application_1520754626920_6227/spark-f3d19076-0642-4db8-961d-99daae0dfaff/fetchFileTemp230224632617642846.tmp
18/03/21 15:36:27 INFO util.Utils: Copying /data/disk01/yarn/nm/usercache/testuser/appcache/application_1520754626920_6227/spark-f3d19076-0642-4db8-961d-99daae0dfaff/-17884647971521635771454_cache to /data/disk01/yarn/nm/usercache/testuser/appcache/application_1520754626920_6227/container_1520754626920_6227_01_000002/./myfile.txt
18/03/21 15:36:28 INFO broadcast.TorrentBroadcast: Started reading broadcast variable 1
18/03/21 15:36:28 INFO client.TransportClientFactory: Successfully created connection to strt01we.ebb.er.com/172.25.235.216:43791 after 4 ms (0 ms spent in bootstraps)
18/03/21 15:36:28 INFO memory.MemoryStore: Block broadcast_1_piece0 stored as bytes in memory (estimated size 6.3 KB, free 366.3 MB)
18/03/21 15:36:28 INFO broadcast.TorrentBroadcast: Reading broadcast variable 1 took 551 ms
答案 0 :(得分:1)
广播变量似乎已加载到内存中,直到它们被明确销毁为止。
相反,sc.addFile
似乎正在为每个执行程序创建到磁盘的副本。因此,我会猜测SparkFiles.get()
会在每次调用 时将文件加载到内存中。
SparkFiles.get()
中调用.map()
,它将尝试为RDD中的每个条目重新加载文件。最后,回答您的问题,
文件将被读取多少次?每个执行者在特定节点上一次?或每个任务一次?
取决于,在何处调用.get
,如上所述。
将执行哪些步骤,如何在执行程序上处理代码?
我不明白这部分。
如何使用更好的addFile或bc var?
这些是不同的用例。例如,考虑一个1GB sqliteDB转储的情况。 Spark可以通过JDBC连接到该数据库对象。它实际上并不需要将整个对象加载到内存中。
spark会基于pyspark代码进行优化并创建隐式BC变量吗?
不确定,但我不这么认为。