シャード化されたデータベースでは、データがシャードキーに基づいて物理シャードに分散されます。シャードキー以外の列をフィルター条件としてクエリを実行すると、PolarDB-X はすべてのシャードをスキャンする必要があります。これは、データ量の増加に伴いコストが増大するフルシャードスキャンです。グローバルセカンダリインデックス(GSI)は、インデックス対象列をシャードキーとして独立してシャード化された別個のインデックステーブルを維持することでこの課題を解決します。これにより、オプティマイザーはクエリを関連するシャードに直接ルーティングできます。
本トピックでは、PolarDB-X で GSI を作成し、クエリの高速化に活用する方法について説明します。
前提条件
開始する前に、以下の条件を満たしていることを確認してください。
MySQL 5.7 以降
PolarDB-X カーネルのマイナーバージョン 5.4.1 以降
グローバルセカンダリインデックスの仕組み
GSI は、以下のコンポーネントで構成されます。
| コンポーネント | 説明 |
|---|---|
| インデックステーブル | GSI 用に作成される別個の分散テーブルであり、インデックス対象列をシャードキーとしてプライマリテーブルとは独立してシャード化されます。 |
| インデックスキー列 | インデックステーブルのシャードキーです。シャーディング句で参照されるすべての列がインデックスキー列になります。 |
| カバリング列 | インデックスキー列に加えて、インデックステーブルに格納される追加の列です。デフォルトでは、プライマリテーブルのプライマリキーおよびすべてのシャードキーが含まれます。クエリで必要な列がすべてインデックステーブルに存在する場合、PolarDB-X はプライマリテーブルへの参照を経ずにインデックステーブルから直接読み取りを行います。これをカバリングインデックス読み取りと呼びます。 |
| シャーディング句 | インデックステーブルをデータベースおよびテーブルパーティションにどのように分散するかを指定します。CREATE TABLE のシャーディング句と同じ構文を使用します。 |
GSI を使用するタイミング:クエリが頻繁にプライマリテーブルのシャードキー以外の列をフィルター条件として使用しており、フルシャードスキャンによってパフォーマンスが低下している場合に GSI を作成します。ほとんどのクエリがシャードキーを直接使用する場合は、GSI を導入してもストレージおよび書き込みのオーバーヘッドのみが発生し、パフォーマンス向上の効果は得られません。
GSI の作成
PolarDB-X は、MySQL のデータ定義言語(DDL)構文を拡張し、GSI の定義をサポートしています。その構文は、標準的な MySQL インデックス作成と同様のパターンに従います。
GSI を追加する方法は、以下の 2 通りがあります。
テーブル作成時に GSI をインラインで定義する
既存のテーブルに
CREATE GLOBAL INDEXを使用して GSI を追加する
以下の例では、t_order テーブルを order_id でシャード化したケースを想定しています。このテーブルには、クエリで頻繁にフィルター条件として使用される buyer_id および seller_id 列も存在し、これらは GSI の適切な候補となります。
-- テーブル作成時に GSI を定義
CREATE TABLE t_order (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`order_id` varchar(20) DEFAULT NULL,
`buyer_id` varchar(20) DEFAULT NULL,
`seller_id` varchar(20) DEFAULT NULL,
`order_snapshot` longtext DEFAULT NULL,
`order_detail` longtext DEFAULT NULL,
PRIMARY KEY (`id`),
GLOBAL INDEX `g_i_seller`(`seller_id`) COVERING (`id`, `order_id`, `buyer_id`, `order_snapshot`) dbpartition by hash(`seller_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 dbpartition by hash(`order_id`);
-- 既存のテーブルに GSI を追加
CREATE UNIQUE GLOBAL INDEX `g_i_buyer` ON `t_order`(`buyer_id`)
COVERING(`seller_id`, `order_snapshot`)
dbpartition by hash(`buyer_id`) tbpartition by hash(`buyer_id`) tbpartitions 3GSI の完全な作成構文およびオプションの詳細については、「CREATE INDEX」をご参照ください。
GSI の使用
GSI を作成した後、クエリでは以下の 3 つの方法で利用できます:オプティマイザーによる自動選択、明示的な HINT 構文の使用、またはインデックステーブルへの直接クエリ実行です。
自動インデックス選択
プライマリテーブルに対するクエリで一致する GSI が存在する場合、PolarDB-X はコストが最も低いインデックスを自動的に選択します。ただし、自動選択の対象となるのはカバリングインデックスのみです。インデックステーブルにクエリで必要なすべての列が含まれていない場合、オプティマイザーはそのインデックスを自動的に選択しません。
以下の例では、クエリが seller_id をフィルター条件として使用し、id および order_snapshot を選択しています。このとき、g_i_seller はこれらの 3 つの列をすべてカバーしており、かつ seller_id がそのシャードキーであるため、オプティマイザーはクエリをインデックステーブルに直接ルーティングし、t_order のプライマリシャードに対するフルスキャンを回避します。
EXPLAIN SELECT t_order.id, t_order.order_snapshot FROM t_order WHERE t_order.seller_id = 's1';
IndexScan(tables="g_i_seller_sfL1_2", sql="SELECT `id`, `order_snapshot` FROM `g_i_seller` AS `g_i_seller` WHERE (`seller_id` = ?)")HINT を使用したインデックス指定
インデックスヒントを使用すると、オプティマイザーの選択をオーバーライドできます。PolarDB-X では、以下の 2 種類の HINT 構文をサポートしています。
`FORCE INDEX` — 指定された名前のインデックスを強制的に使用します。
SELECT a.*, b.order_id
FROM t_seller a
JOIN t_order b FORCE INDEX(g_i_seller) ON a.seller_id = b.seller_id
WHERE a.seller_nick = "abc";TDDL ヒント — コメント形式のディレクティブでインデックスを指定します。
/*+TDDL:INDEX({table_name/table_alias}, {index_name})*/例:
/*+TDDL:index(a, g_i_buyer)*/ SELECT * FROM t_order a WHERE a.buyer_id = 123クエリでインデックステーブルに格納されていない列が必要な場合、PolarDB-X は 2 段階の検索を実行します。まずインデックステーブルをクエリしてプライマリキーおよびプライマリテーブルのシャードキーを取得し、次にそれらを基にプライマリテーブルをクエリして残りの列を取得します。詳細については、「INDEX HINT」をご参照ください。
インデックステーブルへの直接クエリ
クエリで必要なすべての列がインデックステーブルに含まれている場合、プライマリテーブルを介さず、インデックステーブルに対して直接クエリを実行して結果を取得できます。
USE INDEX および IGNORE INDEX によるインデックス選択制御
USE INDEX を使用すると、オプティマイザーの検討対象を特定のインデックスセットに制限できます。また、IGNORE INDEX を使用すると、指定したインデックスを検討対象から除外できます。
-- g_i_seller の使用を禁止
SELECT t_order.id, t_order.order_snapshot FROM t_order IGNORE INDEX(g_i_seller) WHERE t_order.seller_id = 's1';
-- オプティマイザーが g_i_seller のみを検討するよう制限
SELECT t_order.id, t_order.order_snapshot FROM t_order USE INDEX(g_i_seller) WHERE t_order.seller_id = 's1';どちらの構文でも、カンマ区切りのインデックス名リストを指定できます:IGNORE INDEX(index1, index2, ...)。
次のステップ
CREATE INDEX — GSI 作成の完全な構文リファレンス
INDEX HINT — インデックスヒントの構文および動作に関する詳細ガイド