列ファミリがあるとします:
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;
record_link_id
)パフォーマンスに影響しますか?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_id
の9897
エントリを検索し、scopeid=35
とformid=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)から抜粋した次の図を検討してください。
基本的に、ページングはクエリを分割し、結果の次の反復のためにクラスターに戻ります。タイムアウトする可能性は低くなりますが、パフォーマンスは、結果セット全体のサイズとクラスター内のノードの数に比例して低下します。
TL; DR;要求される結果がより多くのノードに広がるほど、時間がかかります。
Cassandra 2.xでは、セカンダリインデックスのみのクエリも可能です。
record_link_id = 9897のupdate_auditから*を選択します。
しかし、これは、分散環境上のすべてのパーティションを読み取るため、データのフェッチに大きな影響を及ぼします。このクエリによって取得されたデータも一貫性がなく、中継できませんでした。
提案:
セカンダリインデックスの使用は、NoSQL Data ModelビューからのDIRTクエリと見なされます。
セカンダリインデックスを回避するために、新しいテーブルを作成し、そこにデータをコピーできます。これはアプリケーションのクエリであるため、テーブルはクエリから派生します。