データベースでは、インデックスを作成することでクエリのパフォーマンスを向上させることができます。データテーブルのデータ量が増えてくると、パーティショニングによってデータテーブルを複数のシャードに分割し、ロードバランシングを用いてこれらのデータシャードをクラスタ全体に分散させることで、クラスタ全体のサービス能力を高めることができます。本記事では主に、ローカルインデックス、グローバルインデックス、一意インデックスについて説明します。
インデックスの種類
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パーティションの方が並列クエリでより大きなパフォーマンス向上が期待できます。
クエリ時のインデックス選択戦略
ユーザーがパーティションプルーニングを重視する場合は、プレフィックスインデックスを選択する方が良いでしょう。クエリのフィルター条件でパーティションプレフィックスの値を指定することで、パーティションプルーニングを最大限に行い、インデックスパーティションデータの読み取りを削減できます。
ユーザーがスループットを重視し、アクセスするデータ量が多い場合は、非プレフィックスインデックスを選択する方がパフォーマンスを向上させることができます。パーティション並列処理により、パーティションキー上の範囲クエリを解決できます。ローカル非プレフィックスインデックスはすべてのパーティションへの並列アクセスが可能ですが、ローカルプレフィックスインデックスはパーティションプルーニングを行い、少数のパーティションが大量のデータセットを処理するため、応答時間が並列クエリよりも悪くなる可能性があります。