データベースでは、インデックスを作成することでクエリのパフォーマンスを向上させることができます。データテーブルのデータ量が増大するにつれて、パーティションを使用してデータテーブルを複数のシャードに分割し、ロードバランシングを利用してデータシャードをクラスタ全体に分散させることで、クラスタ全体のサービス能力を向上させることができます。本記事では、主にローカルインデックス、グローバルインデックス、一意インデックスについて説明します。
インデックスの種類
OceanBaseデータベースでは、主に以下のインデックスタイプが関係します:
ローカルパーティションインデックス:インデックス作成時にキーワード
LOCALを指定したインデックスはローカルインデックスです。ローカルインデックスはパーティションルールを指定する必要がなく、そのパーティション属性は主表の属性と一致し、主表のパーティション操作に伴って変更されます。グローバルパーティションインデックス:インデックス作成時にキーワード
GLOBALを指定したインデックスはグローバルインデックスです。グローバルインデックスはパーティションルールに従ってテーブルを分割できます。一意インデックス:インデックスキー値が一意であることを示し、キーワード
UNIQUEで表されます。プレフィックスインデックス:パーティションインデックステーブルのパーティションキーがインデックス列の左側のプレフィックスである場合、そのインデックスはプレフィックスインデックスと呼ばれます。これはMySQLの長文字列のプレフィックスインデックスとは異なります。例えば、インデックステーブル
idxがc1とc2列に基づいて作成されている場合、そのインデックステーブルのパーティションキーがc1であれば、そのインデックスはプレフィックスインデックスと呼ばれます。パーティションキーがc2または他の列である場合は、非プレフィックスインデックスと呼ばれます。非プレフィックスインデックス:パーティションプレフィックスインデックスに対して、パーティションインデックスがプレフィックスインデックスでない場合、それは非プレフィックスインデックスと呼ばれます。
ローカルインデックス
ローカルインデックスは、パーティションキーに基づいてローカルプレフィックスインデックスとローカル非プレフィックスインデックスに分類されます。
ローカルプレフィックスインデックス
パーティションキーがインデックスの左側のプレフィックスであり、かつインデックスにサブパーティションキーが含まれる場合、このインデックスはローカルプレフィックスインデックスです。ローカルプレフィックスインデックスは一意インデックスまたは非一意インデックスにすることができます。
インデックスキーを指定したクエリは、ローカルプレフィックスインデックスを利用してインデックスの特定のパーティションを一意に特定できるため、結果セットが小さいもののパーティションプルーニングが必要な場合に非常に適しています。
例えば、テーブルAにローカルインデックス idx(c1,c2,c3) が存在する場合、主表は c1 に基づいてパーティション化されます。ローカルインデックスの特性により、インデックステーブルと主表のパーティショニング方法は同じであるため、idx も c1 をパーティションキーとしています。定義によれば、これはローカルプレフィックスインデックスです。インデックスキーを指定したクエリを処理する際、パーティションキー c1 の値によって唯一のインデックスパーティションを特定できるため、インデックスパーティションへのアクセスを大幅に削減できます。
ローカル非プレフィックスインデックス
インデックステーブルがローカルプレフィックスインデックスでない場合、それはローカル非プレフィックスインデックスです。可能性のある状況として、インデックステーブルのパーティションキーがインデックスの左側のプレフィックスではない場合、またはインデックスにサブパーティションキーが含まれていない場合があります。
パーティションキーがインデックスの部分集合でない場合、ローカル非プレフィックスインデックスは一意インデックスにすることはできません。 インデックスキーを指定したクエリは、ローカル非プレフィックスインデックスを利用してインデックスパーティションを特定できず、すべてのインデックスパーティションにアクセスする必要があるため、データ量が多く並行処理を重視するシナリオに適しています。
例えば、テーブルAにローカルインデックス idx(c1,c2,c3) が存在する場合、主表は c4 列に基づいてパーティション化されます。定義によれば、これはローカル非プレフィックスインデックスです。ユーザーがインデックスキーを指定してクエリを実行する際、パーティションキーによってインデックスパーティションを特定できないため、結果を得るためにはすべてのインデックスパーティションにアクセスする必要があります。このようなシナリオでは、並行実行が重要な役割を果たすことができます。
グローバルインデックス
グローバルインデックスは、主表とは独立した独自のパーティション定義を持ちます。同時に、グローバルインデックステーブルのパーティションも分割およびメジャーコンパクションが可能です。 一般的に、主表とグローバルインデックスのパーティショニング方法が完全に同じ場合、一意の非プレフィックスインデックスを除き、他のインデックスはローカルインデックスとして定義することを推奨します。グローバルインデックスは、パーティションの管理とメンテナンスにおいてローカルインデックスよりもはるかに高いコストがかかります。また、クエリコストやパーティションプルーニングの観点から見ると、パーティショニング方法が同じであれば、グローバルインデックスとローカルインデックスの効果は同じです。
グローバルプレフィックスインデックス
グローバルパーティションインデックスの最初のフィールドがテーブルの主パーティションフィールドである場合、そのインデックスはグローバルプレフィックスインデックスです。
グローバルプレフィックスインデックスは、一意インデックスまたは非一意インデックスにすることができます。
グローバルプレフィックスインデックスは、Rangeパーティションでのみ意味をなし、Hashパーティションインデックスには意味がありません。その理由は、ユーザーがHashパーティションインデックスを選択した場合、ユーザーのクエリパターンは必ずインデックスキーによるポイントクエリであるため、インデックスキーがパーティションキーをカバーしている場合、プレフィックスインデックスであるかどうかは意味がなく、ユーザーが指定したインデックスキー値からインデックスパーティションを計算できるからです。ユーザーがすべてのパーティションキー値を指定しない場合、Hashパーティションインデックスはすべてのパーティションデータにアクセスする必要がありますが、Rangeパーティションではある程度のパーティションプルーニングが可能です。
グローバル非プレフィックスインデックス
OceanBaseデータベースはグローバル非プレフィックスインデックスをサポートしていません。グローバル非プレフィックスインデックスは、クエリ最適化にそれほど意味がありません。
例えば、テーブルAにグローバルインデックスidx(c1,c2)があり、idxがc2でパーティションされている場合、idxはグローバル非プレフィックスインデックスです。この場合、ユーザーがすべてのインデックスキー値を指定した場合にのみパーティションプルーニングが可能であり、それ以外の場合はすべてのインデックスパーティションをスキャンする必要があるため、ユーザーはc1キーを直接使用してパーティションを行うことに何の理由もありません。c1を使用することで、プレフィックスフィルタリングによるパーティションプルーニングも可能です。
一意インデックス
一意インデックスは、グローバルインデックスまたはローカルインデックスとして定義できます。
一意インデックスをローカルインデックスとして定義する場合、インデックスキーがインデックスパーティションキーをカバーしているという条件を満たす必要があります。 例えば、テーブルAに一意インデックスidx(c1,c2,c3)があり、インデックステーブルidxが(c1,c2)でパーティションされている場合、同じ(c1,c2)は必ず同一のパーティションに入ることが保証されるため、単一のパーティション内での一意性のみを維持すればよくなります。インデックステーブルidxが(c2,c4)でパーティションされている場合、インデックスがパーティションキーをカバーしていないため、ローカルインデックスではパーティション間のインデックスキーの一意性を保証できません。このようなローカル一意インデックスは作成できません。
インデックス作成戦略
インデックスを作成する際には、ユーザーのクエリパターン、インデックス管理、パフォーマンス、可用性などの要件を総合的に考慮し、自社のビジネスに最適なインデックス方式を選択する必要があります。
ユーザーが一意インデックスを必要とし、かつインデックスキーがすべてのパーティションキーをカバーしている場合は、ローカルインデックスとして定義できます。そうでない場合は、グローバルインデックスを使用する必要があります。
主表のパーティションキーがインデックスの部分集合である場合は、ローカルインデックスを使用できます。
主表のパーティション属性とインデックスのパーティション属性が同じ場合は、ローカルインデックスを使用することを推奨します。
ユーザーがインデックスパーティション管理のコストを重視し、主表のパーティションが常に削減され続ける場合は、グローバルインデックスの作成を避けるようにしてください。これは、主表のパーティション削減操作によりグローバルインデックスの変更が大きくなり、復元が困難になる可能性があり、さらにインデックスが使用不能になる可能性があるためです。
ユーザーのクエリが常にすべてのインデックスパーティションキー値を指定する場合は、パーティションキーを含めずに他の列にインデックスを作成するだけで済み、メンテナンスコストとストレージコストを削減できます。パーティションキーを指定しないクエリについては、プレフィックスインデックスはパーティションプルーニングやデータ量が比較的小さいシナリオに適しており、非プレフィックスインデックスは一般的にパーティション並列処理やデータ量が比較的大きいシナリオに適しています。
パーティション選択戦略
インデックスパーティションを行う際、単一のインデックステーブルのデータ量が大きすぎる場合は、パーティションを行って、クエリのパーティション並列処理とロードバランシングを改善する必要があります。
ユーザーのクエリが指定インデックスキーによるポイントクエリが多い場合、アクセスするパーティション数とアクセスの並行性の観点から、HashパーティションとRangeパーティションのクエリコストは大差ありません。ただし、データにホットスポットが存在する場合、Hashパーティションの方がホットスポット問題を回避できます。
ユーザーのクエリが指定インデックスキーによる範囲クエリが多い場合、アクセスするパーティション数の観点からは、Rangeパーティションの方がHashパーティションより優れています。しかし、並行性の観点から考えると、Hashパーティションの方が並行クエリの利点を活かせ、クエリ結果セットが大きい場合、Hashパーティションの並行クエリの方が大きなパフォーマンス優位性を発揮できるはずです。
クエリ時のインデックス選択戦略
ユーザーがパーティションプルーニングを重視する場合は、プレフィックスインデックスを選択する方が良いでしょう。クエリのフィルタ条件でパーティションプレフィックスの値を指定することで、パーティションプルーニングを最大限に行い、インデックスパーティションデータの読み取りを削減できます。
ユーザーがスループットを重視し、アクセスするデータ量が大きい場合は、非プレフィックスインデックスを選択する方がパフォーマンスを向上させることができます。パーティション並列処理により、パーティションキーに対する範囲クエリを解決できます。ローカル非プレフィックスインデックスはすべてのパーティションに並行してアクセスできますが、ローカルプレフィックスインデックスはパーティションプルーニングを行い、少数のパーティションが大量のデータセットを処理するため、応答時間は並行クエリよりも悪くなる可能性があります。