4. BetaSeries en chiffres
• 10 ans d'activité
• 1 036 351 membres
• 16 848 séries TV
• 12 244 361 séries suivies
• 237 811 épisodes vus par jour
• 567 millions d'épisodes vus depuis 10 ans 😱😭🔫
• plus les notes, commentaires, téléchargements etc.
5.
6. BetaSeries Insights
• Utiliser la richesse et le volume des données de
BetaSeries.
• Vendre ces données aux pros des médias
(producteurs, diffuseurs, journalistes etc.).
• Analyse d'audience en temps réel.
• Permettre des analyses et comparaisons complexes :
“ Est-ce que Game of Thrones marche mieux que
The Walking Dead chez les moins de 30 ans qui
regardent aussi Big Bang Theory ? ”
7. Le choix de la BDD
(source : Dilbert par Scott Adams)
8. Pourquoi Neo4j ?
• C'est assez simple à mettre en place, à interroger
et à maintenir (cypher 😍).
• Les bases en graphes permettent de modéliser
facilement un schéma qui évolue.
• C'est très performant quand on travaille sur des
petits sous ensembles du graphe (une série en
particulier ou un type d'audience).
• Ça s'intègre très bien avec PHP : graphaware/
neo4j-php-client
10. #1. Chaque jour des statistiques figées
sur les données depuis le début de
BetaSeries jusqu'à la journée de la veille.
11. Avantages
• On peut récupérer le différentiel des données une
fois par jour (via un cron).
• Une fois une statistique calculée, on peut la mettre
en cache pour la journée (on utilise un cache Redis).
• Il n'y a pas un grand besoin de performances en
écriture, puisqu'on écrit plus dans Neo4j.
• Il n'y a pas non plus besoin de performances en
lecture, puisque tout est en cache (sauf au premier
calcul du jour sur une statistique donnée).
12. #2. On a besoin régulièrement
d'importer le total des données de
BetaSeries.
13. Pourquoi ?
• BetaSeries est complexe, on peut avoir des
problèmes de fiabilité des données importées qui
demandent un ré-import.
• Le projet Insights est jeune et évolue vite, on doit
rajouter des statistiques régulièrement, dont des
nouveaux types de noeuds et relations.
• Il fallait bien importer les 10 ans de données une
première fois quoi qu'il arrive.
• C'est une sécurité supplémentaire de pouvoir recréer
complètement la BDD en cas de panne.
15. • Le serveur MySQL est répliqué sur plusieurs slaves.
• Sur le serveur physique d'Insights on retrouve un slave dédié, un
serveur Neo4j et une base Redis.
• Le slave est utilisé pour récupérer les données pendant l'import.
• Mais aussi pour récupérer des metadonnées de BetaSeries
(nom, description, images des séries etc.).
17. Insights V1
• ma connaissance de Neo4j qui se réduisait à la doc.
• l'utilisation d'une machine non dédiée et limitée en ressources.
• une approche d'export / import non appropriée au volume des données.
Le premier “Proof of Concept” d'Insights était limité par :
19. Export de MySQL vers CSV
• Première approche : un script
PHP par noeud ou relation qui
fait la requête SQL et qui génère
le fichier CSV.
• Plus performant : directement
générer le fichier CSV avec
SELECT INTO OUTFILE.
21. Importer dans Neo4j
• Ne pas oublier de renseigner la
configuration du dossier d'import
dbms.directories.import=/tmp
• On utilisera Cypher avec
LOAD CSV WITH HEADERS
• On créé le noeud Show
correspondant en faisant bien
attention aux types des données
(tout est une chaîne dans un CSV)
22. Même principe pour les relations
• On commence à avoir un temps
d'export plus long (60 secondes
pour 12M de lignes) et un CSV
plus lourd (190 mo)
• On peut gzip le fichier pour
gagner de la place (36 mo), mais
ça prends un peu de temps.
23. Les ennuis commencent 😩
• On a déjà bien du mal à importer 1M
de lignes dans Neo4j (4 minutes).
• Si on utilise pas USING PERIODIC
COMMIT on explose la RAM de
Neo4j.
• On va devoir faire un import par lot
(12 imports, 1 million de lignes à la
fois).
25. Pas avec LIMIT et OFFSET 👎
• Mettons qu'on veuille
récupérer 1 million d'épisodes
vus, à partir du 500 millionième
vu, la requête prend 18,7
secondes à s'exécuter.
• Plus la table est grosse et plus
on va avancer dans l'OFFSET,
plus la requête sera longue.
26. En passant par un index 👍
• En passant par un index on
passe à 95 ms !
• Il faut donc calculer les bornes
pour chaque passage du
traitement par lot.
• (on peut tricher sur les id)
29. C'est bien, mais pas top.
• C'est pratique, on peut utiliser
presque les mêmes CSV.
• Les metadonnées doivent être dans
les headers, il faut donc changer le
header de chaque CSV.
• L'import ne peut se faire que sur
une base éteinte, il faut couper
neo4j pendant l'import.
• Pas hyper clair au niveau de la
tolérance aux fautes.
30. Et nos 567 millions d'épisodes vus ?
J'aurais préféré les oublier. 😭
31. Pas possible. ☠
• En faisant une extrapolation à partir de l'export / import des 12 millions de
relations des séries suivies...
• On arrive à 47 minutes d'export, un fichier CSV de 9go (difficilement
gzipable) et un import de 37,8 heures ! 😨
• Sans compter le serveur Neo4j de la V1 qui n'aurait probablement pas
tenu la charge des 567 millions de relations.
32. Pour réduire le volume des données à importer on
n'importe pas le détail des actions (type épisodes
vus) comme une relation, mais on dénormalise sur
une propriété countWatched du noeud Episode.
(il y a une perte d'information)
42. Quand on sait pas faire…
on demande à ceux qui savent.
43. Amélioration de l'import journalier avec apoc
• apoc est un ensemble de
procédures pour Neo4j.
• apoc possède de nombreuses
procédures d'accès aux données
(JSON, XML et... JDBC !)
• import / export deux en un et
optimisé, parfait pour notre
export journalier.
49. L'API BatchInserter
• L'API BatchInserter est le moyen le plus performant pour créer une base
de données Neo4j à partir de zéro.
• Elle est contenue dans l'API Java de Neo4j.
• Un nouveau fichier graph.db est directement créé sur le filesystem.
• On peut aussi travailler sur un fichier graph.db existant (base éteinte),
mais ce n'est pas mon cas.
53. ⚠ ne pas oublier ⚠
• Bien penser à couper la réplication du Slave MySQL pendant l'import.
• Une fois le fichier graph.db créé dans un dossier temporaire on coupe neo4j,
on fait un backup du fichier graph.db actuel et on le remplace par le nouveau
fichier.
• Bien penser à mettre les droits neo4j:adm en récursif sur graph.db
• Relancer neo4j.
• Exécuter des scripts post import.
• Warmup les données comme pour l'import journalier.
56. V1 -> V2
• Nombre de noeuds : 2.3M -> 18M : +680%
• Nombre de relations : 13M -> 650M : +4900%
• Nombre de propriétés : 18M -> 640M : +3450%
• Taille du store : 1.8gb -> 46gb : +2440%
57. À propos du page cache
• Pour de meilleurs performances, il faut
que tout le store soit contenu dans le
page cache (et donc en RAM) :
dbms.memory.pagecache.size=64g
• Après avoir relancé le serveur
(typiquement après un import total) il
faut bien penser à warmup le page
cache.
CALL apoc.warmup.run(true);
58. Le futur d'Insights 🚀
• Pas trop de changement dans le schéma de base.
• Mais des données évolutives dans Neo4j (classements, cibles, etc.).
• Des stats comparatives entre séries, pool de séries, cibles, etc.
• De l'analyse prédictive.
• De l'analyse sémantique.
• Un moteur de recommandation pour BetaSeries ?
59. Conclusion bateau 🚤
• Il faut toujours utiliser les outils adaptés
pour résoudre un problème. 🛠
• Quitte à se faire un peu violence. 😡
• À condition de les connaître bien sûr. 🙃