web-dev-qa-db-fra.com

cassandra obtenir tous les enregistrements dans la plage de temps

Je dois travailler avec une famille de colonnes dont la clé est (user_id, timestamp). Dans ma requête, j'aimerais extraire tous les enregistrements d'une plage de temps donnée, indépendamment de l'identifiant de l'utilisateur. C'est le schéma exact de la table:

CREATE TABLE userlog (
  user_id text,
  ts timestamp,
  action text,
  app_type text,
  channel_name text,
  channel_session_id text,
  pid text,
  region_id text,
  PRIMARY KEY (user_id, ts)
)

J'ai essayé de courir

SELECT * FROM userlog  WHERE ts >= '2013-01-01 00:00:00+0200' AND  ts <= '2013-08-13 23:59:00+0200' ALLOW FILTERING;

qui fonctionne bien sur mon installation locale de cassandra contenant un petit ensemble de données, mais échoue avec

Request did not complete within rpc_timeout.

sur le système de production contenant toutes les données.

Existe-t-il une requête, de préférence cql, qui fonctionne sans à-coups avec la famille de colonnes donnée ou de modifier la conception?

23
Faber

Le délai est dû au fait que Cassandra a pris plus de temps que le délai (10 secondes par défaut) pour renvoyer les données. Pour votre requête, Cassandra tentera d'extraire l'intégralité du jeu de données avant de le retourner. Pour plusieurs enregistrements, cela peut facilement prendre plus de temps que le délai d'attente.

Pour les requêtes produisant beaucoup de données, vous devez paginer, par exemple.

SELECT * FROM userlog WHERE ts >= '2013-01-01 00:00:00+0200' AND  ts <= '2013-08-13 23:59:00+0200' AND token(user_id) > previous_token LIMIT 100 ALLOW FILTERING;

user_id est le précédent user_id renvoyé. Vous devrez également consulter la page ts pour vous assurer que tous les enregistrements du dernier user_id ont été renvoyés.

Alternativement, dans Cassandra 2.0.0 (qui vient de paraître), la pagination est faite de manière transparente, de sorte que votre requête initiale doit fonctionner sans délai d'expiration ou pagination manuelle.

Le ALLOW FILTERING signifie que Cassandra lit toutes vos données, mais ne les renvoie que dans la plage spécifiée. Ceci n'est efficace que si la plage correspond à la plupart des données. Si vous vouliez trouver des enregistrements dans, par exemple, une fenêtre de 5 minutes, ce serait très inefficace.

31
Richard

Il semble que le hotness pour pouvoir interroger par heure (ou n’importe quelle plage) consiste à spécifier une "autre colonne" en tant que clé de partition, puis de spécifier l’horodatage en tant que "colonne de mise en cluster".

CREATE TABLE postsbyuser (
     userid bigint,
     posttime timestamp,
     postid uuid,
     postcontent text,
     PRIMARY KEY ((userid), posttime)
   ) WITH CLUSTERING ORDER BY (posttime DESC);

insérer de fausses données

  insert into postsbyuser (userid, posttime) values (77, '2013-04-03 07:04:00');

et query (l’important étant qu’il s’agit d’une requête "rapide" et que ALLOW FILTERING n’est pas obligatoire, comme il se doit):

  SELECT * FROM postsbyuser where userid=77 and posttime > '2013-04-03 07:03:00' and posttime < '2013-04-03 08:04:00';

Vous pouvez également utiliser des astuces pour grouper par jour (et donc pouvoir interroger par jour) ou non.

Si vous utilisez le style "groupe par jour", un index secondaire serait également une option (bien que les index secondaires ne semblent fonctionner qu'avec l'opérateur "EQ" =?).

2
rogerdpack

En général, cela peut indiquer que vous n'avez pas modélisé votre schéma pour répondre à votre requête de données, ce qui est la façon de faire de Cassandra ( https://docs.datastax.com/fr/cql/3.3/cql /ddl/dataModelingApproach.html ) ...

Donc, idéalement, vous devriez modéliser votre schéma pour répondre à la requête. Il existe des ressources sur la manière de modéliser des séries chronologiques pour Cassandra, bien que, par exemple, Ce diaporama semble être similaire à ce que vous avez obtenu - mais il ne s’agit pas d’un support publicitaire pour le type de requête que vous souhaitez effectuer. Je ne pense pas avoir réellement trouvé d'exemples de schémas Cassandra qui prennent en charge les requêtes "obtenez-moi toutes les données pour une certaine période".

En tout cas, pour le reste de cette réponse, je supposerai que vous êtes coincé avec le schéma que vous avez pour cette itération.

Vous pouvez le faire comme deux requêtes:

SELECT DISTINCT user_id FROM userlog;

Et puis, pour chaque utilisateur,

SELECT * FROM userlog WHERE
  user_id='<user>'
  AND ts >= '2013-01-01 00:00:00+0200'
  AND ts <= '2013-08-13 23:59:00+0200';

Si l'ensemble des ID utilisateur est de taille petite à moyenne, vous pourrez peut-être utiliser une requête IN:

SELECT * FROM userlog WHERE
  user_id IN ('sampleuser', 'sampleadmin', ...)
  AND ts >= '2013-01-01 00:00:00+0200'
  AND ts <= '2013-08-13 23:59:00+0200';

Notez que cela fonctionne sansALLOW FILTERING.

0
m01