1

次の CQL テーブルがあります (わかりやすくするために少し簡略化しています)。

CREATE TABLE test_table (
    user        uuid,
    app_id      ascii,
    domain_id   ascii,
    props       map<ascii,blob>,
    PRIMARY KEY ((user), app_id, domain_id)
)

このテーブルには多くのユーザー (つまり、数千万行など) が含まれるという考えです。ユーザーごとに関心のあるドメインがいくつかあり、ドメインごとにいくつかのアプリがあります。また、各ユーザー/ドメイン/アプリには、小さなプロパティ セットがあります。

このテーブル全体をスキャンし、その内容を特定の app_id と domain_id のチャンクにロードする必要があります。私の考えは、 TOKEN 関数を使用して、データセット全体を数回繰り返して読み取ることができるようにすることでした。したがって、次のようなものです。

SELECT props FROM test_table WHERE app_id='myapp1'
  AND domain_id='mydomain1'
  AND TOKEN(user) > -9223372036854775808
  AND TOKEN(user) < 9223372036854775807;

行キーの範囲を指定し、クラスタリング キーの値を指定することで列範囲を効果的に指定するため、このクエリは効率的であると想定していました。しかし、このクエリを実行しようとすると、「Bad Request: このクエリを実行できません。データのフィルタリングが必要なため、パフォーマンスが予測できない可能性があります。パフォーマンスが予測できないにもかかわらず、このクエリを実行したい場合は、ALLOW FILTERING を使用してください」というエラー メッセージが表示されます。 .

私は Cassandra の経験が限られており、この種のクエリは get_range_slices() 呼び出しにマップされ、スライス述語 (つまり、app_id/domain_id 値で定義された列の範囲) とトークン範囲で定義されたキー範囲を受け入れると想定しました。 . この種のクエリの処理方法を誤解しているか、get_range_slices() 呼び出しの効率について誤解しているようです。

より具体的には、私の質問は次のとおりです。-このデータモデルが私が考えている種類のクエリに対して意味がある場合-このクエリが効率的であると予想される場合-効率的である場合、なぜこのエラーメッセージが表示されるのですかフィルタリングを許可する

最後のものについての私の唯一の推測は、指定された app_id/domain_id の組み合わせを持たない行は結果からスキップする必要があるということでした。

- - アップデート - -

すべてのコメントに感謝します。私はこれについてさらに調査を行ってきましたが、まだ完全には理解していないことがあります。

指定された構造では、取得しようとしているのは、データセットからの長方形の領域のようなものです (すべての行に同じ列があると仮定します)。長方形の上下はトークン範囲 (範囲) によって決定され、左側/右側は列範囲 (スライス) として定義されます。したがって、これは自然に get_range_slices リクエストに変換されます。CQL が ALLOW FILTERING 句を配置する必要がある理由は、探している列を含まない行があるため、スキップする必要があるという私の理解 (間違っている場合は訂正してください)。そして、(指定された範囲内で) 私の基準に適合する行を見つける前に、2 行ごとにスキップする必要があるのか​​、それとも最初の 100 万行をスキップする必要があるのか​​ は誰にもわからないため、これが予測できないレイテンシーやタイムアウトの原因となります。私は正しいですか?同じ種類のクエリを実行するテストを作成しようとしましたが、低レベルの Astyanax API を使用しました (同じテーブルに対して、CQL で生成されたデータを読み取る必要がありましたが、非常に単純であることが判明しました)。このテストは機能します。 -行に要求している列のスライスが含まれていない列のないキーを返すことを除いて。もちろん、開始トークンに基づいてある種の単純なページングを実装し、小さなチャンクでデータをフェッチするように制限する必要がありました。

今、私は疑問に思っています-繰り返しますが、何十万ものユーザーに対処する必要があることを考えると、このテーブルを部分的に「回転」させて、次のように整理する方がよいでしょうか:

行キー: domain_id + app_id + partition no (hash(user) mod X のようなもの) クラスタリング キー: column partition no (hash(user) >> 16 mod Y のようなもの) + user

「列パーティション番号」について...本当に必要かどうかはわかりません。このモデルを使用すると、ドメインとアプリの組み合わせごとに比較的少数の行 (X=1000..10000) になると思います。これにより、必要に応じて並行して、個々のパーティションに対してクエリを実行できます。ただし、(ユーザーがランダムな UUID であると仮定すると) 1 億ユーザーの場合、1 行あたり数十または数十万の列になります。1 回のリクエストでそのような行を 1 つ読み取るのは良い考えですか? それはCassandraにいくらかのメモリプレッシャーを与えたに違いありません。では、それらをグループ (Y=10..100 など) で読む方がよいでしょうか?

私がやろうとしていることは、Cassandra がうまくやっていることではないことを認識しています - 異なるホストから並列フェッチするために、事前に計算できるチャンク (トークン範囲やパーティション キーなど) で CF データの「すべて」または大きなサブセットを読み取ります。しかし、そのようなユースケースで最も効率的なパターンを見つけようとしています。

ちなみに、「select * from ... where TOKEN(user)>X and TOKEN(user)」のようなクエリは、

4

2 に答える 2

5

簡潔な答え

この警告は、Cassandra がインデックスのないデータを読み取り、基準を満たさない行を除外する必要があることを意味します。クエリの最後に追加ALLOW FILTERINGすると機能しますが、大量のデータをスキャンします。

SELECT props FROM test_table 
WHERE app_id='myapp1' 
AND domain_id='mydomain1' 
AND TOKEN(user) > -9223372036854775808 
AND TOKEN(user) < 9223372036854775807
ALLOW FILTERING;

長い説明

この例では、プライマリ キーは 2 つの部分で構成されています。userパーティション キーとして使用され、<app_id, domain_id>残りの部分を形成します。さまざまなユーザーの行がクラスター全体に分散され、各ノードが特定の範囲のトークン リングを担当します。

単一ノードの行は、パーティション キーのハッシュによって並べ替えられます (このtoken(user)例では)。1 人のユーザーのさまざまな行が 1 つのノードに格納され、<app_id, domain_id>タプルで並べ替えられます。

したがって、主キーはツリーのような構造を形成します。パーティション キーは階層の 1 つのレベルを追加し、主キーの残りの各フィールドは別のレベルを追加します。デフォルトでは、Cassandra は、ツリーの連続した範囲 (key in (...)構造を使用する場合は複数の範囲) からすべての行を返すクエリのみを処理します。Cassandra が一部の行を除外するALLOW FILTERING必要がある場合は、指定する必要があります。

を必要としないクエリの例ALLOW FILTERING:

SELECT * FROM test_table 
WHERE user = 'user1'; 
//OK, returns all rows for a single partition key

SELECT * FROM test_table 
WHERE TOKEN(user) > -9223372036854775808 
AND TOKEN(user) < 9223372036854775807; 
//OK, returns all rows for a continuos range of the token ring

SELECT * FROM test_table 
WHERE user = 'user1'
AND app_id='myapp1'; 
//OK, the rows for specific user/app combination 
//are stored together, sorted by domain_id field

SELECT * FROM test_table 
WHERE user = 'user1'
AND app_id > 'abc' AND app_id < 'xyz'; 
//OK, since rows for a single user are sorted by app

を必要とするクエリの例ALLOW FILTERING:

SELECT props FROM test_table 
WHERE app_id='myapp1';
//Must scan all the cluster for rows, 
//but return only those with specific app_id

SELECT props FROM test_table 
WHERE user='user1'
AND domain_id='mydomain1';
//Must scan all rows having user='user1' (all app_ids), 
//but return only those having specific domain

SELECT props FROM test_table 
WHERE user='user1'
AND app_id > 'abc' AND app_id < 'xyz'
AND domain_id='mydomain1';
//Must scan the range of rows satisfying <user, app_id> condition,
//but return only those having specific domain

何をすべきか?

Cassandra では、プライマリ キーの部分にセカンダリ インデックスを作成することはできません。いくつかのオプションがあり、それぞれに長所と短所があります。

  • 主キーを持つ別のテーブルを追加し((app_id), domain_id, user)、必要なデータを 2 つのテーブルに複製します。app_id特定または<app_id, domain_id>組み合わせについて必要なデータをクエリできます。特定のドメインとすべてのアプリをクエリする必要がある場合は、3 番目のテーブルが必要です。このアプローチはマテリアライズド ビューと呼ばれます
  • ある種の並列処理 (hadoop、spark など) を使用して、すべてのアプリとドメインの組み合わせに対して必要な計算を実行します。いずれにせよ Cassandra はすべてのデータを読み取る必要があるため、おそらく単一のペアと大きな違いはありません。後で使用するために他のペアの結果がキャッシュされる可能性がある場合は、おそらく時間が節約されます。
  • ALLOW FILTERING必要に応じてクエリのパフォーマンスが許容できる場合にのみ使用してください。何千万ものパーティション キーは、Cassandra にとっておそらく多すぎません。
于 2013-10-17T00:05:00.260 に答える