web-dev-qa-db-ja.com

Cassandraでセカンダリインデックスはどのように機能しますか?

列ファミリがあるとします:

CREATE TABLE update_audit (
  scopeid bigint,
  formid bigint,
  time timestamp,
  record_link_id bigint,
  ipaddress text,
  user_zuid bigint,
  value text,
  PRIMARY KEY ((scopeid, formid), time)
  ) WITH CLUSTERING ORDER BY (time DESC)

2つのセカンダリインデックスの場合、record_link_idは高カーディナリティの列です。

CREATE INDEX update_audit_id_idx ON update_audit (record_link_id);

CREATE INDEX update_audit_user_zuid_idx ON update_audit (user_zuid);

私の知識によるとCassandraは次のような2つの非表示列ファミリを作成します。

CREATE TABLE update_audit_id_idx(
    record_link_id bigint,
    scopeid bigint,
    formid bigint,
    time timestamp
    PRIMARY KEY ((record_link_id), scopeid, formid, time)
);

CREATE TABLE update_audit_user_zuid_idx(
    user_zuid bigint,
    scopeid bigint,
    formid bigint,
    time timestamp
    PRIMARY KEY ((user_zuid), scopeid, formid, time)
);

Cassandraセカンダリインデックスは、通常のテーブルのように分散されるのではなく、ローカルインデックスとして実装されます。各ノードは、格納するデータのインデックスのみを格納します。

次のクエリを検討してください。

select * from update_audit where scopeid=35 and formid=78005 and record_link_id=9897;
  1. このクエリは、Cassandraの「内部」でどのように実行されますか?
  2. 高カーディナリティの列インデックス(record_link_id)パフォーマンスに影響しますか?
  3. Cassandraは上記のクエリのすべてのノードにタッチしますか?理由?
  4. 最初に実行される基準は、ベーステーブルのpartition_keyまたはセカンダリインデックスのpartition_keyですか? Cassandra=これら2つの結果はどのように交差しますか?
35
Aftab
select * from update_audit where scopeid=35 and formid=78005 and record_link_id=9897;

上記のクエリはcassandraの内部でどのように機能しますか?

基本的に、パーティションscopeid=35およびformid=78005のすべてのデータが返され、record_link_idインデックスでフィルタリングされます。 record_link_id9897エントリを検索し、scopeid=35formid=78005で返された行に一致するエントリを一致させようとします。パーティションキーとインデックスキーの行の共通部分が返されます。

高カーディナリティ列(record_link_id)インデックスは、上記のクエリのクエリパフォーマンスにどのように影響しますか?

高カーディナリティインデックスは、基本的に、メインテーブルの(ほぼ)各エントリの行を作成します。 Cassandra=はクエリ結果の順次読み取りを実行するように設計されています。インデックスクエリは基本的にCassandra= ランダムインデックス値のカーディナリティが増加すると、クエリ値の検索にかかる時間も増加します。

cassandraは上記のクエリのすべてのノードに触れますか?なぜですか?

いいえ。scopeid=35およびformid=78005パーティションを担当するノードにのみアクセスする必要があります。インデックスも同様にローカルに保存され、ローカルノードに有効なエントリのみが含まれます。

高カーディナリティの列にインデックスを作成することは、最速かつ最高のデータモデルになります

ここでの問題は、アプローチが拡大縮小せず、update_auditが大きなデータセットの場合は遅くなることです。 MVPリチャードローには、セカンダリインデックスに関する素晴らしい記事があります( The Sweet Spot For Cassandra Secondary Indexing )、特にこの点について:

テーブルがメモリよりも大幅に大きい場合、クエリは数千の結果を返すだけでも非常に遅くなります。潜在的に数百万のユーザーを返すことは、たとえそれが効率的なクエリであるように見えても、悲惨です。

...

実際には、これは、数十個、場合によっては数百個の結果を返すために、インデックス付けが最も役立つことを意味します。次にセカンダリインデックスの使用を検討する場合は、このことに留意してください。

これで、特定のパーティションで最初に制限するアプローチが役立ちます(パーティションがメモリに確実に収まる必要があるため)。しかし、ここでは、セカンダリインデックスに依存する代わりに、record_link_idをクラスタリングキーにする方がパフォーマンスが優れていると思います。

編集

プライマリキーを提供する場合でも、何百万人ものユーザーがいるときに低カーディナリティインデックスにインデックスを設定する方法

行の幅によって異なります。非常に低いカーディナリティインデックスに関する注意すべき点は、返される行の%が通常大きいことです。たとえば、幅の広いusersテーブルを考えます。クエリのパーティションキーで制限しますが、10,000行が返されます。インデックスがgenderのようなものにある場合、クエリはそれらの行の約半分を除外する必要があり、これはうまく機能しません。

セカンダリインデックスは、(詳細な説明がないため)「道路の中間」カーディナリティで最適に機能する傾向があります。上記の幅の広いusersテーブルの例を使用すると、countryまたはstateのインデックスは、genderのインデックスよりもはるかに優れたパフォーマンスを発揮します(それらのユーザーのほとんどは、すべて同じ国または州に住んでいるわけではありません)。

20180913を編集

最初の質問「上記のクエリはcassandraで内部的にどのように機能しますか?」に対する回答については、ページネーションを使用したクエリの動作を知っていますか?

Java Driver documentation (v3.6)から抜粋した次の図を検討してください。

enter image description here

基本的に、ページングは​​クエリを分割し、結果の次の反復のためにクラスターに戻ります。タイムアウトする可能性は低くなりますが、パフォーマンスは、結果セット全体のサイズとクラスター内のノードの数に比例して低下します。

TL; DR;要求される結果がより多くのノードに広がるほど、時間がかかります。

47
Aaron

Cassandra 2.xでは、セカンダリインデックスのみのクエリも可能です。

record_link_id = 9897のupdate_auditから*を選択します。

しかし、これは、分散環境上のすべてのパーティションを読み取るため、データのフェッチに大きな影響を及ぼします。このクエリによって取得されたデータも一貫性がなく、中継できませんでした。

提案:
セカンダリインデックスの使用は、NoSQL Data ModelビューからのDIRTクエリと見なされます。

セカンダリインデックスを回避するために、新しいテーブルを作成し、そこにデータをコピーできます。これはアプリケーションのクエリであるため、テーブルはクエリから派生します。

2
Swam Guru