1. 概要

このチュートリアルでは、 ApacheCassandraでセカンダリインデックスを使用する方法について説明します。

データベース内でデータがどのように分散されているかを確認し、すべてのインデックスタイプを調べます。 最後に、セカンダリインデックスを使用するためのいくつかのベストプラクティスと推奨事項について説明します。

2. カサンドラアーキテクチャ

Cassandraは、完全に分散化された通信モデルを備えたNoSQL分散データベースです。

同等の役割を持つ複数のノードで構成され、高可用性を提供します。 あらゆるクラウドプロバイダーとオンプレミスで実行できるため、クラウドに依存しません。

また、単一のCassandraクラスターを複数のクラウドプラットフォームに同時にデプロイすることもできます。 これは、応答速度が重要であり、ほとんど変更されない単純なクエリを使用するOLTP(オンライントランザクション処理)クエリに最適です。

2.1. 主キー

主キーは、データレコードを一意に識別する最も重要なデータモデリングの選択肢です少なくとも1つのパーティションキーと0個以上のクラスタリング列で構成されています。

パーティションキーは、クラスター全体でデータを分割する方法を定義します。 クラスタリング列は、高速読み取り操作を可能にするためにディスク上のデータを順序付けます。

例を見てみましょう:

CREATE TABLE company (
    company_name text,
    employee_name text,
    employee_email text,
    employee_age int,
    PRIMARY KEY ((company_name), employee_email)
);

ここでは、 company_name を、テーブルデータをノード間で均等に分散するために使用されるパーティションキーとして定義しました。 次に、クラスタリング列として employee_email を指定したため、Cassandraはこれを使用して、行を効率的に取得するために各ノードでデータを昇順で保持します。

2.2. クラスタートポロジ

Cassandraは、使用可能なノードの数に正比例する線形のスケーラビリティとパフォーマンスを提供します

ノードはリング状に配置されてデータセンターを形成し、地理的に分散した複数のデータセンターを接続することでクラスターを作成します。

Cassandraは、手動の介入なしにデータを自動的に分割するため、ビッグデータの準備が整います。

次に、Cassandraがcompany_nameによってテーブルをどのように分割するかを見てみましょう。

ご覧のとおり、 company テーブルは、パーティションキー company_name を使用してパーティションに分割され、ノード全体に分散されます。 Cassandraが同じcompany_name値で行をグループ化し、ディスク上の同じ物理パーティションに格納していることがわかります。 その結果、最小限のI/Oコストで特定の企業のすべてのデータを読み取ることができます。

さらに、レプリケーションファクターを定義することで、データセンター全体にデータをレプリケートできます。 レプリケーション係数Nは、クラスター内のN個の異なるノードに各データ行を格納します。

レプリカの数は、クラスターレベルではなく、データセンターレベルで指定できます。 その結果、複数のデータセンターのクラスターを作成でき、各データセンターのレプリケーション係数は異なります。

3. 非主キーのクエリ

前に定義したcompanyテーブルを取得し、employee_ageで検索してみましょう。

SELECT * FROM company WHERE employee_age = 30;

InvalidRequest: Error from server: code=2200 [Invalid query] message="Cannot execute this query as it might involve data filtering and thus may have unpredictable performance. If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING"

ALLOW FILTERING 句を使用しない限り、主キーの一部ではない列をクエリできないため、このエラーメッセージが表示されます。

ただし、技術的に可能であっても、ALLOW FILTERINGは高価で時間がかかるため、本番環境で使用しないでください。 これは、バックグラウンドで、クラスター内のすべてのノードで全表スキャンを開始して結果をフェッチするためです。これは、パフォーマンスに悪影響を及ぼします。

ただし、これを使用できる1つの許容可能なユースケースは、単一のパーティションで多くのフィルタリングを実行する必要がある場合です。 この場合、Cassandraは引き続きテーブルスキャンを実行しますが、単一ノードに制限できます。

SELECT * FROM company WHERE company_name = 'company_a' AND employee_age = 30 ALLOW FILTERING;

company_name クラスタリング列を条件として追加したため、Cassandraはそれを使用して、すべての会社データを保持するノードを識別します。 その結果、その特定のノードのテーブルデータに対してのみテーブルスキャンを実行します。

4. 二次インデックス

Cassandraのセカンダリインデックスは、主キーの一部ではない列をクエリする必要性を解決します。

データを挿入するとき、Cassandraは変更を保存するために commitlog と呼ばれる追加専用ファイルを使用するため、書き込みは迅速です。 同時に、データはMemtableと呼ばれるキー/列値のメモリ内キャッシュに書き込まれます。 Cassandraは定期的に、Memtableを不変のSSTableの形式でディスクにフラッシュします。

次に、Cassandraの3つの異なるインデックス作成方法を見て、長所と短所について説明します。

4.1. 通常のセカンダリインデックス(2i)

通常のセカンダリインデックスは、非プライマリキー列でクエリを実行するために定義できる最も基本的なインデックスです。

employee_age列にセカンダリインデックスを定義しましょう。

CREATE INDEX IF NOT EXISTS ON company (employee_age);

これで、employee_ageによるクエリをエラーなしで実行できるようになりました。

SELECT * FROM company WHERE employee_age = 30; 

company_name  | employee_email    | employee_age | employee_name 
--------------+-------------------+--------------+---------------
    company_A | emp1@companyA.com |           30 |     employee_1

インデックスを設定すると、Cassandraはインデックスデータをバックグラウンドで保存するための非表示のテーブルを作成します。

CREATE TABLE company_by_employee_age_idx ( 
    employee_age int,
    company_name text,
    employee_email text,
    PRIMARY KEY ((employee_age), company_name, employee_email) 
);

通常のテーブルとは異なり、Cassandraはクラスター全体のパーティショナーを使用して非表示のインデックステーブルを配布しません。 インデックスデータは、同じノード上のソースデータと同じ場所に配置されます。

したがって、セカンダリインデックスを使用して検索クエリを実行すると、Cassandraはすべてのノードからインデックス付きデータを読み取り、すべての結果を収集します。 クラスタに多数のノードがある場合、これによりデータ転送が増加し、レイテンシが高くなる可能性があります。

Cassandraが主キーに基づいてノード間でインデックステーブルを分割しない理由を自問するかもしれません。 答えは、ソースデータと一緒にインデックスデータを保存すると、レイテンシが減少するということです。 また、インデックスの更新はネットワーク経由ではなくローカルで実行されるため、接続の問題によって更新操作が失われるリスクはありません。 さらに、Cassandraは、インデックス列のデータが均等に分散されていない場合、ワイドパーティションの作成を回避します。

セカンダリインデックスが付加されたテーブルにデータを挿入すると、CassandraはインデックスとベースMemtableの両方に書き込みます。 さらに、両方がSSTableに同時にフラッシュされます。 その結果、インデックスデータはソースデータとは別のライフサイクルを持ちます。

セカンダリインデックスに基づいてデータを読み取る場合、Cassandraは最初にインデックス内の一致するすべての行のプライマリキーを取得し、その後、それらを使用してソーステーブルからすべてのデータをフェッチします。

4.2. SSTable-添付セカンダリインデックス(SASI)

SASIは、SSTableライフサイクルをインデックスにバインドするという新しいアイデアを導入しています。 インメモリインデックスを実行した後、 SSTable を使用してインデックスをディスクにフラッシュすると、ディスク使用量が削減され、CPUサイクルが節約されます。

SASIインデックスを定義する方法を見てみましょう。

CREATE CUSTOM INDEX IF NOT EXISTS company_by_employee_age ON company (employee_age) USING 'org.apache.cassandra.index.sasi.SASIIndex';

SASIの利点は、トークン化されたテキスト検索、高速範囲スキャン、およびメモリ内インデックス作成です。 一方、欠点は、特にテキストのトークン化を有効にする場合に、大きなインデックスファイルが生成されることです。

最後に、 DataStax Enterprise (DSE)のSASIインデックスは実験的なものであることに注意してください。 DataStaxは、本番用のSASIインデックスをサポートしていません。

4.3. Storage-Attached Indexing(SAI)

Storage-Attached Indexingは、DataStaxAstraおよびDataStaxEnterpriseデータベースで使用できる拡張性の高いデータインデックスメカニズムです。 任意の列に1つ以上のSAIインデックスを定義してから、範囲クエリ(数値のみ)、 CONTAIN のセマンティクス、およびフィルタークエリを使用できます。

SAIは、列ごとに個別のインデックスファイルを格納し、ソースデータのオフセットへのポインタをSSTableに含めます。 インデックス付きの列にデータを挿入すると、最初にメモリに書き込まれます。 Cassandraは、メモリからディスクにデータをフラッシュするたびに、データテーブルとともにインデックスを書き込みます。

このアプローチでは、書き込みのオーバーヘッドが削減されるため、スループットが43 % andレイテンシーが230% over2i向上します。 SASIおよび2iと比較すると、インデックス作成に使用するディスクスペースが大幅に少なく、障害点が少なく、アーキテクチャが簡素化されています。

SAIを使用してインデックスを定義しましょう。

CREATE CUSTOM INDEX ON company (employee_age) USING 'StorageAttachedIndex' WITH OPTIONS = {'case_sensitive': false, 'normalize': false};

正規化オプションは、特殊文字を基本文字に変換します。 たとえば、ドイツ語の文字öを通常のoに正規化して、特殊文字を入力せずにクエリの照合を有効にすることができます。 たとえば、「schon」を条件として使用するだけで、「schön」という用語を検索できます。

4.4. ベストプラクティス

まず、クエリでセカンダリインデックスを使用する場合、条件としてパーティションキーを追加することをお勧めします。 その結果、読み取り操作を単一ノード(および整合性レベルに応じてレプリカ)に減らすことができます。

SELECT * FROM company WHERE employee_age = 30 AND company_name = "company_A";

次に、クエリをパーティションキーのリストに制限し、結果のフェッチに関与するノードの数を制限できます。

SELECT * FROM company WHERE employee_age = 30 AND company_name IN ("company_A", "company_B", "company_C");

第3に、結果のサブセットのみが必要な場合は、クエリに制限を追加できます。 これにより、読み取りパスに含まれるノードの数も減ります。

SELECT * FROM company WHERE employee_age = 30 LIMIT 10;

さらに、カーディナリティが非常に低い列(性別、true / false列など)にセカンダリインデックスを定義することは避けなければなりません。これは、パフォーマンスに影響を与える非常に広いパーティションを生成するためです。

同様に、カーディナリティの高い (社会保障番号、電子メールなど)は、非常に細かいパーティションのインデックスになり、最悪の場合、クラスターコーディネーターがすべてをヒットするようになります。プライマリレプリカ。

最後に、頻繁に更新される列にセカンダリインデックスを使用しないようにする必要があります。この背後にある理論的根拠は、Cassandraが不変のデータ構造を使用し、頻繁に更新されるとディスクへの書き込み操作の数が増えるためです。

5. 結論

この記事では、Cassandraがデータセンター全体でデータを分割する方法と、3種類のセカンダリインデックスについて説明しました。

セカンダリインデックスを検討する前に、データを2番目のテーブルに非正規化し、頻繁にアクセスする予定がある場合は、メインテーブルで最新の状態に保つことを検討する必要があります。

一方、データアクセスが散発的である場合、別のテーブルを追加すると、不当な複雑さが追加されます。 したがって、セカンダリインデックスを導入することをお勧めします。 間違いなく、ストレージに接続されたインデックスは、私たちが持っている3つのインデックスオプションの中から最良の選択であり、最高のパフォーマンスと簡素化されたアーキテクチャを提供します。