有1亿客户在几个网站的页面上点击1000亿次(假设有100个网站)。点击流可在大型数据集中使用。
使用Apache Spark的抽象,每个网站计算不同访问者的最有效方法是什么?
答案 0 :(得分:39)
visitors.distinct().count()
将是显而易见的方式,第一种方式是明确的,您可以指定并行度,也可以看到速度的提高。如果可以将访问者设置为流并使用D流,则可以实时计算。您可以直接从目录流式传输,并使用与RDD相同的方法,如:
val file = ssc.textFileStream("...")
file.distinct().count()
最后一个选项是使用def countApproxDistinct(relativeSD: Double = 0.05): Long
但是这被标记为实验性的,但如果relativeSD
(标准偏差)更高,则会明显快于计数。
编辑:由于您希望每个网站的计数只能减少网站ID,因此可以有效地完成(使用合并器),因为计数是聚合的。如果您有一个RDD的网站名称用户ID元组,您可以这样做。
visitors.countDistinctByKey()
或visitors.countApproxDistinctByKey()
,大约一个是实验性的。要使用大约不同的键,您需要PairRDD
有趣的附注如果你对近似值没问题并希望得到快速结果,你可能需要研究由与火花放大器实验室相同的人制作的blinkDB。
答案 1 :(得分:10)
我必须做类似的事情,你可以做的一件有效的事情(那不是真正的火花)是将你的游客ID映射到字节列表而不是GUID字符串,你可以节省4倍的空间(作为2个字符)是单字节的十六进制编码,字符串中的Char使用2个字节。
// Inventing these custom types purely for this question - don't do this in real life!
type VistorID = List[Byte]
type WebsiteID = Int
val visitors: RDD[(WebsiteID, VisitorID)] = ???
visitors.distinct().mapValues(_ => 1).reduceByKey(_ + _)
请注意,您也可以这样做:
visitors.distinct().map(_._1).countByValue()
但这也不会扩展。
答案 2 :(得分:9)
我注意到,在RDD上运行它时,基本的不同功能可以比在DataFrame集合上运行它快得多。例如:
DataFrame df = sqlContext.load(...)
df.distinct.count // 0.8 s
df.rdd.distinct.count // 0.2 s
答案 3 :(得分:8)
如果data
是RDD
(网站,访问者)对,则data.countApproxDistinctByKey(0.05)
将为您提供RDD
(网站,计数)。可以减少参数以获得更高的准确性,但代价是更多的处理。
答案 4 :(得分:6)
Spark 2.0将ApproxCountDistinct添加到数据帧和SQL API中:
答案 5 :(得分:4)
如果您想要每个网页,那么visitors.distinct()...
效率低下。如果有很多访问者和大量网页,那么您就可以区分大量的(webpage, visitor)
组合,这可能会压倒内存。
这是另一种方式:
visitors.groupByKey().map {
case (webpage, visitor_iterable)
=> (webpage, visitor_iterable.toArray.distinct.length)
}
这要求单个网页的访问者适合内存,因此在所有情况下可能都不是最好的。
答案 6 :(得分:3)
Sim给了a great discussion about the count distinct problem at the Spark Summit in Europe。
HyperLogLog algo最适合需要逐步更新的大量不同计算。
Tagar链接到的ApproxCountDistinct算法并不是最好的算法,因为它们不公开底层的HLL草图且无法重新汇总(Sim在他的演讲中对此进行了讨论)。
This blog post介绍了如何使用spark-alchemy库创建可重新聚合的HLL草图。好玩的东西!
答案 7 :(得分:0)
Df.select(approx_count_distinct("col_name",0.1))
0.1是表示允许的最大估计误差的参数。 使用大数据集,您会看到很多出色的性能。