2014-09-16 3 views
3

J'essaie de traduire une exigence relativement commune dans SQL à un modèle de données efficace dans Cassandra. J'essaie de décider de la meilleure façon de modéliser mes données afin que je puisse commander mes lignes dans Cassandra dans le même ordre que je souhaite les signaler dans l'application. Normalement, ce serait un bon cas pour une colonne de mise en cluster, sauf que les données par lesquelles je veux commander mon résultat sont une métrique qui sera mise à jour plusieurs fois par jour. Je vais expliquer le problème en SQL et ensuite partager les approches de modélisation de données qui me sont venues à l'esprit. Ce que je voudrais savoir, c'est que quelqu'un a fait face à une exigence similaire à la mienne et, si oui, comment avez-vous modélisé les données dans Cassandra.Accès efficace des résultats ordonnés dans Cassandra

Voici le problème que j'essaie de résoudre.

Supposons que j'avoir une table raw_data définie comme ceci:

CREATE TABLE raw_data (
    A varchar, 
    B varchar, 
    C varchar, 
    D varchar, 
    ts timestamp, 
    val varint 
    PRIMARY KEY (ts,A,B,C,D) 
); 

Et j'ai aussi un tableau récapitulatif

CREATE TABLE summary_table (
    A varchar, 
    B varchar, 
    C varchar, 
    total_val varint 
    PRIMARY KEY (A,B,C) 
); 

Lorsque les données dans mon tableau récapitulatif sont agrégées par ma demande d'une manière qui correspond à

SELECT A, B, C, SUM(val) FROM raw_data GROUP BY A, B, C 

ce que je veux être en mesure de faire est d'exécuter une requête comme suivant:

SELECT B, C, total_val FROM summary_table WHERE A = "Something" ORDER BY total_val DESC LIMIT 1000; 

C'est à dire, je veux sous-ensemble mon tableau récapitulatif pour une valeur particulière de A, puis retourner les 1000 premières lignes, commandés par total_val

Total_val est mis à jour toutes les quelques minutes de mon application, car des données supplémentaires sont diffusées dans ma table raw_data. Je ne peux donc pas utiliser total_val comme colonne de classification pour mes données

Ce que j'essaie de décider, c'est la meilleure façon de modéliser ce type de problème dans Cassandra - dans lequel je dois sous-classer un tableau récapitulatif avec un WHERE CLAUSE et ordonner le jeu de résultats (qui sont constamment mis à jour) dans l'ordre DESC. On peut s'attendre à ce que certains ensembles de résultats soient assez grands - plusieurs centaines de milliers de lignes (c'est-à-dire qu'il y a des valeurs pour A dans mon tableau récapitulatif pour lesquelles SELECT COUNT(*) FROM summary_table WHERE A = "some value" serait très, très grand, dans les centaines de milliers). Il est évidemment inefficace de trier ces données et de les rejeter avant de les envoyer à mon application.

En outre, cela ne semble pas être un bon cas d'utilisation pour les indices secondaires. Sur les ensembles de résultats plus petits, ils sont très performants. Pour les plus grands, ils sont à la traîne et je soupçonne qu'il pourrait y avoir une meilleure façon de gérer ce problème. Une autre façon dont j'ai envisagé de modéliser cela consiste à mettre en mémoire cache les jeux de résultats plus volumineux en mémoire, de sorte qu'au moins, où je devrais trier plusieurs milliers de lignes, je le ferais au moins en mémoire. J'ai également envisagé d'avoir une table récapitulative secondaire qui est déjà pré-remplie avec les 1000 premières lignes que je veux exposer à mon application ... bien que je ne puisse pas penser à un bon moyen de garder ces données à jour et d'éviter le exact même problème que j'ai avec ma table récapitulative originale.

Quelqu'un a-t-il rencontré un problème comme celui-ci, dans lequel vous devez filtrer vos données récapitulatives avec une clause WHERE et ordonner vos résultats (changeant fréquemment) dans l'ordre Desc? Si oui, avez-vous trouvé un moyen de rendre cela performant lorsque certaines clauses WHERE retourneraient plusieurs milliers de lignes? Si oui, comment l'avez-vous fait?

Répondre

4

La meilleure façon que je peux penser à faire serait la suivante:

CREATE TABLE summary_table (
    time_bucket long, 
    A varchar, 
    total_val int, 
    timestamp long, 
    B varchar, 
    C varchar, 
    PRIMARY KEY ((time_bucket, A), total_val, timestamp, B, C) 
) WITH CLUSTERING ORDER BY (total_val DESC); 

Avec cette structure, vous n'écrasez pas vraiment total_val. Au lieu de cela, vous insérez une nouvelle ligne pour chaque nouvelle valeur, puis annulez tout sauf l'horodatage le plus récent au moment de la requête. La valeur de time_bucket doit être votre horodatage arrondi à un intervalle que vous pouvez calculer au moment de la requête (vous devrez peut-être interroger plusieurs compartiments à la fois, mais essayez de limiter ce nombre à deux si possible). Dans le cas où vous vous demandez, time_bucket et A devenir votre clé de partition, ce qui empêche la croissance illimitée de la ligne au fil du temps. En d'autres termes, vous avez transformé votre tableau récapitulatif en données de série chronologique. Si besoin est, vous pouvez ajouter un TTL aux vieilles colonnes afin qu'elles meurent naturellement. Tant que vos tranches de temps sont saines, vous ne rencontrerez pas le problème de l'interrogation d'un grand nombre de pierres tombales.

+0

"vous avez transformé votre tableau récapitulatif en données de séries temporelles ...." C'est vraiment une idée brillante, Robbie. Je vous dois une bière la prochaine fois que ATL Cassandra Meetup arrive. Je pense à environ 20 façons différentes d'appliquer quelque chose comme ça ... c'est absolument une idée fantastique. – evanv

+3

Heureux d'être utile! En fait, un principe général pour modéliser des données avec Cassandra est d'écrire de façon immuable. Rappelez-vous que Cassandra est un système de stockage structuré en journal, il vous suffit donc de convertir vos données pour qu'elles correspondent à des données de journal, c'est-à-dire des séries chronologiques. –

Questions connexes