web-dev-qa-db-ja.com

SELECT DISTINCT cqlはWHERE句を無視します

2つの同一のリクエストを実行しますが、DISTINCTキーワードは予期しない結果をもたらします。キーワードがない場合、結果は問題ありませんが、DISTINCTでは、where句は無視されます。どうして ?

Cqlshバージョン:

Connected to Test Cluster at localhost:9160.
[cqlsh 4.1.1 | Cassandra 2.0.6 | CQL spec 3.1.1 | Thrift protocol 19.39.0]

考慮される表:

DESCRIBE TABLE events;

CREATE TABLE events (
  userid uuid,
  "timestamp" timestamp,
  event_type text,
  data text,
  PRIMARY KEY (userid, "timestamp", event_type)
) WITH
  bloom_filter_fp_chance=0.010000 AND
  caching='KEYS_ONLY' AND
  comment='' AND
  dclocal_read_repair_chance=0.000000 AND
  gc_grace_seconds=864000 AND
  index_interval=128 AND
  read_repair_chance=0.100000 AND
  replicate_on_write='true' AND
  populate_io_cache_on_flush='false' AND
  default_time_to_live=0 AND
  speculative_retry='99.0PERCENTILE' AND
  memtable_flush_period_in_ms=0 AND
  compaction={'class': 'SizeTieredCompactionStrategy'} AND
  compression={'sstable_compression': 'LZ4Compressor'};

テーブルの内容:

SELECT * FROM events;

 userid                               | timestamp                | event_type | data
--------------------------------------+--------------------------+------------+------
 aaaaaaaa-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:06:17+0100 |       toto | null
 4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:06:17+0100 |       toto | null
 4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:07:17+0100 |       toto | null
 4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:08:17+0100 |       toto | null
 4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:09:17+0100 |       toto | null
 4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:10:17+0100 |       toto | null

(6 rows)

リクエスト1:DISTINCTなしのリクエスト

SELECT userid FROM events WHERE timestamp > '1970-01-17 09:07:17+0100' ALLOW FILTERING;

 userid
--------------------------------------
 4271a78f-be1c-44ab-a0e8-f25cf6064b0e
 4271a78f-be1c-44ab-a0e8-f25cf6064b0e
 4271a78f-be1c-44ab-a0e8-f25cf6064b0e

(3 rows)

リクエスト2:DISTINCTと同じリクエスト

SELECT DISTINCT userid FROM events WHERE timestamp > '1970-01-17 09:07:17+0100' ALLOW FILTERING;

 userid
--------------------------------------
 aaaaaaaa-be1c-44ab-a0e8-f25cf6064b0e
 4271a78f-be1c-44ab-a0e8-f25cf6064b0e

(2 rows)

編集1
コンテキストを次に示します。

このバッチスクリプトには2つのニーズがあります。
1-過去5分間アクティブだったすべてのユーザーIDを取得します(つまり、過去5分間のイベントに存在するすべてのユーザーID)
2-それらのユーザーIDに関連するすべてのイベントを取得します(最後の5分間だけではありません)

これを処理するために2つの異なるテーブルを使用していました。最初のリクエスト用の1つのテーブル「activeusers」と、2番目のリクエスト用にここで説明したような「イベント」テーブル。私の問題は、イベントを受け取ったときにサーバーから2つの異なるテーブルに書き込む必要があることだけです。そこで、イベントテーブルのみを使用してこれを試しました。

14
Diplow

In Cassandra CQL DISTINCTはテーブル(列ファミリ)のパーティション(行)キーのみを返すように設計されているため...これは一意でなければなりません。したがって、WHERE句はDISTINCTを使用する場合にのみパーティションキーを操作します(この場合、これは非常に便利ではありません)DISTINCTを取り出すと、WHEREを使用して各パーティションキー内のクラスタリング(列)キーを評価できます(ただし、 ALLOW FILTERING)。

ALLOW FILTERINGは多くのことを行うべきものではなく、間違いなく本番環境ではないことを言及することを強いられます。そのクエリが頻繁に実行する必要がある場合(特定のuseridsの後にtimestampのイベントをクエリする場合)、代わりにevent_typeでデータをパーティション分割することをお勧めします。

PRIMARY KEY (event_type, "timestamp", userid)

次に、ALLOW FILTERINGなしでこのクエリを実行できます。

SELECT userid FROM events WHERE event_type='toto' AND timestamp > '1970-01-17 09:07:17+0100'

アプリケーションやユースケースについて何も知らなくても、それはあなたにとって役に立つかもしれません。しかし、それを例として、またクエリパターンを満たすためにモデルを構築するより良い方法があるかもしれないことを示すものとして考えてください。 Patrick McFadinの時系列データモデリングに関する記事 をチェックして、この問題をモデル化する方法の詳細を確認してください。

20
Aaron

アーロンが説明したように、DISTINCTキーワードを使用する場合、パーティションキーでのみフィルターできます。この背後にある理由は、DISTINCTクエリの背後にあるアルゴリズムと、Cassandra=がデータをディスク/メモリに保存する方法です。

これを理解するために、私は類推します:

Cassandraは、書籍のインデックスと同様の情報を保存します。 「私の3番目の章」という章を検索する場合は、その索引の最初のレベルを調べるだけでよいため、比較的小さなセットで反復検索を行うだけで済みます。ただし、「私の2番目の章」に属する「私の4番目の副章」というサブチャプターを探している場合、インデックスに少なくとも2がある場合、2つの異なるセットで2回の反復検索を行う必要があります。レベル。深くする必要があるほど、時間がかかります(インデックスの先頭にある場合は幸運であり、非常に高速であることがわかりますが、この種のアルゴリズムでは平均および最悪のシナリオをテストする必要があります)より複雑なインデックスが必要になります。

Cassandraは、キースペース->テーブル->パーティションキー->クラスタリングキー->列に似たようなことを行います。深くするほど、メモリ内に必要なセットが多くなり、何かを見つけるのに時間がかかります。 DISTINCTクエリの実行に使用されるインデックスには、パーティションキーレベルまでのセットのみが含まれている場合があり、パーティションキーの検索のみが許可されます。

サブチャプターを含むチャプターを検索すると、「私の2番目のサブチャプター」と呼ばれます(クエリと同様)が、2レベルの深さのインデックスと2レベルの反復検索が必要になることを理解する必要があります。

クラスタリングキーでDISTINCTの使用をサポートすることに決めた場合、クエリは問題ありません。その間、おそらくsetと呼ばれる組み込み型または繰り返し値を単独で処理する類似の何かを使用して、アプリケーションでそれらをフィルタリングする必要があります。

また、Aaronが提案したソリューション(タイムスタンプの後にクラスタリングキーとしてユーザーIDを使用)も、これ(クライアント側でのフィルタリング)もDISTINCT高速メカニズムを使用しません。彼の提案は、クライアント側のフィルタリングを既に処理しているため、クライアント側のフィルタリングを必要としませんが、2つの主な欠点を提供します。テーブルを再作成する必要があるため、下位互換性を提供せず、一定のパーティションキーを使用するため、許可されませんCassandraは、このデータをノード間で分散します。同じパーティションキーのすべての値が同じノードに格納されることに注意してください。

5
Adirio