我对Apache Spark(版本1.6)相对较新,我觉得我遇到了障碍:我查看了SE上与Spark相关的大部分问题,但到目前为止我找不到任何帮助我的东西。我相信我在基础层面上做了一些根本性的错误,但是我不能指出它到底是什么,特别是因为我编写的其他代码片段运行得很好。
我会尝试尽可能具体地解释我的情况,尽管我会简化我的任务以便更好地理解。请记住,正如我仍在学习的那样,我使用Spark的本地模式运行此代码;另外值得注意的是,我一直在使用DataFrames(而不是RDD)。最后,请注意以下代码是使用Pyspark用Python编写的,但我确实欢迎使用Scala或Java的可能解决方案,因为我认为这个问题非常基础。
我有一个通用的JSON文件,其结构类似于以下内容:
{"events":[
{"Person":"Alex","Shop":"Burger King","Timestamp":"100"},
{"Person":"Alex","Shop":"McDonalds","Timestamp":"101"},
{"Person":"Alex","Shop":"McDonalds","Timestamp":"104"},
{"Person":"Nathan","Shop":"KFC","Timestamp":"100"},
{"Person":"Nathan","Shop":"KFC","Timestamp":"120"},
{"Person":"Nathan","Shop":"Burger King","Timestamp":"170"}]}
我需要做的是计算同一个人两次访问同一商店之间的时间。输出应该是至少有一个客户至少每5秒访问一次的商店列表,以及满足此要求的客户数量。在上面的例子中,输出应该如下所示:
{"Shop":"McDonalds","PeopleCount":1}
我的想法是为每个对(Person,Shop)分配相同的标识符,然后继续验证该对是否符合要求。可以使用窗口函数 ROW_NUMBER()来分配标识符,这需要在Spark中使用 hiveContext 。这是上面的文件在分配标识符后的样子:
{"events":[
{"Person":"Alex","Shop":"Burger King","Timestamp":"100","ID":1},
{"Person":"Alex","Shop":"McDonalds","Timestamp":"101", "ID":2},
{"Person":"Alex","Shop":"McDonalds","Timestamp":"104", "ID":2},
{"Person":"Nathan","Shop":"KFC","Timestamp":"100","ID":3},
{"Person":"Nathan","Shop":"KFC","Timestamp":"120","ID":3},
{"Person":"Nathan","Shop":"Burger King","Timestamp":"170","ID":4}]}
由于我需要在得出结论之前为每一对执行几个步骤(其中一些需要使用 self join ),所以我使用了临时表。
我写的代码是这样的(当然,我只包括相关部分 - " df"代表"数据框"):
t1_df = hiveContext.read.json(inputFileName)
t1_df.registerTempTable("events")
t2_df = hiveContext.sql("SELECT Person, Shop, ROW_NUMBER() OVER (order by Person asc, Shop asc) as ID FROM events group by Person, Shop HAVING count(*)>1") #if there are less than 2 entries for the same pair, then we can discard this pair
t2_df.write.mode("overwrite").saveAsTable("orderedIDs")
n_pairs = t2_df.count() #used to determine how many pairs I need to inspect
i=1
while i<=n_pairs:
#now I perform several operations, each one displaying this structure
#first operation...
query="SELECT ... FROM orderedIDs WHERE ID=%d" %i
t3_df = hiveContext.sql(query)
t3_df.write.mode("overwrite").saveAsTable("table1")
#...second operation...
query2="SELECT ... FROM table1 WHERE ..."
t4_df = hiveContext.sql(query2)
temp3_df.write.mode("overwrite").saveAsTable("table2")
#...and so on. Let us skip to the last operation in this loop, which consists of the "saving" of the shop if it met the requirements:
t8_df = hiveContext.sql("SELECT Shop from table7")
t8_df.write.mode("append").saveAsTable("goodShops")
i=i+1
#then we only need to write the table to a proper file
output_df = hiveContext.sql("SELECT Shop, count(*) as PeopleCount from goodShops group by Shop")
output_df.write.json('output')
现在,问题出现了:输出是正确的。我已尝试过多次输入,在这方面,该程序运行良好。然而,它非常缓慢:分析每对需要大约15-20秒,无论每对都有条目。因此,例如,如果有10对需要大约3分钟,如果有100对需要30分钟,依此类推。我在具有相对不错的硬件的几台机器上运行此代码,但没有任何改变。 我也试过缓存我使用的一些(甚至全部)表,但问题仍然存在(在某些情况下所需的时间甚至增加)。更具体地说,循环的最后一个操作(使用&#34;追加&#34;的那个)需要几秒钟才能完成(从5到10),而前6个只需要1-2秒(仍然是很多,考虑到任务的范围,但绝对更易于管理)。
我认为问题可能在于以下一个(或多个):
这三个是我想到的唯一的东西,因为我使用Spark写的其他软件(我没有遇到任何性能问题)没有使用上述技术,因为我基本上执行了简单的JOIN操作,并使用 registerTempTable 方法来使用临时表(根据我的理解,不能在循环中使用)而不是 saveAsTable 方法。
我试图尽可能清楚,但如果您确实需要更多详细信息,我将提供更多信息。
编辑:感谢zero323的答案,我设法解决了我的问题。事实上,使用LAG功能是我真正需要做的事情。另一方面,我已经学会了使用&#34; saveAsTable&#34;应该不鼓励使用这种方法 - 特别是在循环中 - 因为每次调用时都会导致性能大幅下降。除非绝对必要,否则我将从现在开始避免使用它。答案 0 :(得分:1)
同一个人两次访问同一家商店的时间已经过去了多长时间。输出应该是至少有一个客户至少每5秒访问一次的商店列表,以及满足此要求的客户数量。
如何使用聚合简单lag
:
from pyspark.sql.window import Window
from pyspark.sql.functions import col, lag, sum
df = (sc
.parallelize([
("Alex", "Burger King", "100"), ("Alex", "McDonalds", "101"),
("Alex", "McDonalds", "104"), ("Nathan", "KFC", "100"),
("Nathan", "KFC", "120"), ("Nathan", "Burger King", "170")
]).toDF(["Person", "Shop", "Timestamp"])
.withColumn("Timestamp", col("timestamp").cast("long")))
w = (Window()
.partitionBy("Person", "Shop")
.orderBy("timestamp"))
ind = ((
# Difference between current and previous timestamp le 5
col("Timestamp") - lag("Timestamp", 1).over(w)) <= 5
).cast("long") # Cast so we can sum
(df
.withColumn("ind", ind)
.groupBy("Shop")
.agg(sum("ind").alias("events"))
.where(col("events") > 0)
.show())
## +---------+------+
## | Shop|events|
## +---------+------+
## |McDonalds| 1|
## +---------+------+