web-dev-qa-db-fra.com

Nombre efficace distinct avec Apache Spark

100 millions de clients cliquent 100 milliards de fois sur les pages de quelques sites Web (par exemple 100 sites). Et le flux de clics est disponible dans un grand jeu de données.

En utilisant les abstractions d’Apache Spark, quel est le moyen le plus efficace de compter les visiteurs distincts par site Web?

42
Antoine CHAMBILLE

visitors.distinct().count() serait le moyen le plus évident. Avec le premier moyen, vous pouvez spécifier le niveau de parallélisme et voir une amélioration de la vitesse. S'il est possible de configurer les visiteurs en tant que flux et d'utiliser des D-stream, le décompte sera effectué en temps réel. Vous pouvez diffuser directement depuis un répertoire et utiliser les mêmes méthodes que sur le RDD comme:

val file = ssc.textFileStream("...") file.distinct().count()

La dernière option consiste à utiliser def countApproxDistinct(relativeSD: Double = 0.05): Long, mais ceci est qualifié d'expérimental, mais serait beaucoup plus rapide que count si relativeSD (écart std) est plus élevé.

EDIT: Etant donné que vous voulez le nombre par site Web, vous pouvez simplement réduire l'identifiant du site Web, ceci peut être fait efficacement (avec des combineurs) puisque le nombre est agrégé. Si vous avez un RDD de nom d'utilisateur de site Web, vous pouvez le faire. visitors.countDistinctByKey() ou visitors.countApproxDistinctByKey(), encore une fois, la méthode approximative est expérimentale. Pour utiliser approximativement distinctement par clé, vous avez besoin d'un PairRDD

Note latérale intéressante si vous êtes d'accord avec les approximations et que vous voulez des résultats rapides que vous voudrez peut-être examiner blinkDB , créé par les mêmes personnes que spark amp labs.

40
aaronman

J'ai eu à faire des choses similaires, une chose efficace que vous pouvez faire (qui ne soit pas vraiment une étincelle) est de mapper vos identifiants de vistor sur des listes d'octets plutôt que GUID Des chaînes, vous pouvez économiser 4x space alors (car 2 caractères est le codage hexadécimal d'un seul octet, et un caractère utilise 2 octets dans une chaîne).

// 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(_ + _)

Notez que vous pourriez aussi faire:

visitors.distinct().map(_._1).countByValue()

mais cela ne va pas aussi bien.

10
samthebest

J'ai remarqué que la fonction distincte de base peut être considérablement plus rapide lorsque vous l'exécutez sur un RDD que sur une collection DataFrame. Par exemple:

DataFrame df = sqlContext.load(...)
df.distinct.count // 0.8 s
df.rdd.distinct.count // 0.2 s
9
markus

Si data est un RDD de paires (site, visiteur), alors data.countApproxDistinctByKey(0.05) vous donnera un RDD de (site, nombre). Le paramètre peut être réduit pour obtenir plus de précision au prix d'un traitement plus long.

8
Sean Owen
6
Tagar

Si vous le souhaitez par page Web, alors visitors.distinct()... est inefficace. S'il y a beaucoup de visiteurs et de pages Web, vous vous démarquez d'un grand nombre de (webpage, visitor) _ combinaisons pouvant surcharger la mémoire.

Voici un autre moyen:

visitors.groupByKey().map { 
  case (webpage, visitor_iterable)
  => (webpage, visitor_iterable.toArray.distinct.length)
}

Cela nécessite que les visiteurs d'une seule page Web tiennent dans la mémoire, de sorte qu'ils ne seront peut-être pas meilleurs dans tous les cas.

4
foghorn