グローバルインデックスとは、パーティションテーブルの非パーティションキー列に作成されるインデックスです。グローバルインデックスは、一意制約を適用することもできます。
適用範囲
この機能は、マイナーエンジンバージョンが 2.0.14.9.15.0 以降の PolarDB for PostgreSQL PostgreSQL 14 を実行するクラスターでサポートされています。
マイナーエンジンバージョン番号は、コンソールで表示するか、SHOW polardb_version; 文を実行して確認できます。マイナーエンジンバージョンが要件を満たしていない場合は、マイナーエンジンバージョンをアップグレードできます。
背景情報
ビジネスデータの増加に伴い、データパーティショニングはエンタープライズレベルのデータベースの重要な機能となり、データ規模を縮小するための重要な手法となっています。パーティションテーブルは、パーティションキーに基づいて複数の独立した子テーブルに分割されます。子テーブルは個別に管理され、管理性、全体的なパフォーマンス、負荷分散が向上します。
PolarDB for PostgreSQL および の多くのユーザーは、パーティションテーブルを使用してデータを管理しています。典型的なユースケースは、時間によるデータパーティショニングです。
パーティションテーブルのパーティションキーとして時間を使用します。
週次や月次など、定期的に新しいサブパーティションを作成して新しいデータを格納します。
古いサブパーティションを定期的にアーカイブして、パーティションテーブルの運用保守 (O&M) コストを削減します。
上記のシナリオでは、時間は通常パーティションキーとして使用され、プライマリキーや一意の ID としては使用されません。これにより、2 つの問題が発生します。
非パーティションキーを使用してデータをクエリする場合、システムはどのパーティションにデータが含まれているかを判断できないため、すべてのサブパーティションをスキャンする必要があります。
非パーティションキーを使用してデータを変更する場合、パーティションテーブル全体でデータの一意性を保証できません。
これらの問題を解決するために、PolarDB for PostgreSQL および はグローバルインデックス機能を提供します。デフォルトで各サブパーティションに作成されるローカルインデックスとは異なり、単一のグローバルインデックスはパーティションテーブル全体のデータをカバーします。つまり、1 つのインデックスが複数のサブパーティションに対応します。これにより、グローバルな一意性制約を適用し、非パーティションキーでのクエリパフォーマンスを大幅に向上させることができます。
制限事項
グローバルインデックスを持つパーティションテーブルからサブパーティションを
ATTACHまたはDETACHすることは引き続き可能です。`CREATE INDEX` 文で
GLOBALキーワードを指定して、グローバルインデックスを作成します。このキーワードを指定しない場合、デフォルトでローカルインデックスが作成されます。CONCURRENTLYキーワードを指定することで、グローバルインデックスを同時に作成できます。非パーティションテーブルやサブパーティションを含む子テーブルには、グローバルインデックスを作成できません。
グローバルインデックスは式インデックスをサポートしていません。
パーティションテーブルのパーティションキー列にグローバルインデックスを作成することはできません。
構文
グローバルインデックスを作成できます。
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] name ] ON [ ONLY ] table_name [ USING method ]
( { column_name | ( expression ) } [ COLLATE collation ] [ opclass [ ( opclass_parameter = value [, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ INCLUDE ( column_name [, ...] ) ]
[ WITH ( storage_parameter [= value] [, ... ] ) ]
[ GLOBAL/LOCAL ]
[ TABLESPACE tablespace_name ]
[ WHERE predicate ]例
非パーティションキーのクエリの高速化
時間列をパーティションキーとして持つパーティションテーブルを作成できます。
CREATE TABLE partition_range ( id INT, a INT, b INT, created_date TIMESTAMP WITHOUT TIME ZONE ) PARTITION BY RANGE (created_date); CREATE TABLE partition_range_part01 PARTITION OF partition_range FOR VALUES FROM (MINVALUE) TO ('2020-01-01 00:00:00'); CREATE TABLE partition_range_part02 PARTITION OF partition_range FOR VALUES FROM ('2020-01-01 00:00:00') TO ('2020-02-01 00:00:00'); CREATE TABLE partition_range_part03 PARTITION OF partition_range FOR VALUES FROM ('2020-02-01 00:00:00') TO ('2020-03-01 00:00:00');非パーティションキーの条件に基づいてパーティションテーブルをクエリできます。
EXPLAIN (COSTS OFF) SELECT * FROM partition_range WHERE id = 1;結果から、すべてのサブパーティションがスキャンされていることがわかります。パーティションプルーニング機能は使用できません。
QUERY PLAN ------------------------------------------------------------ Append -> Seq Scan on partition_range_part01 partition_range_1 Filter: (id = 1) -> Seq Scan on partition_range_part02 partition_range_2 Filter: (id = 1) -> Seq Scan on partition_range_part03 partition_range_3 Filter: (id = 1) (7 rows)パーティションテーブルにローカルインデックスを作成し、クエリを再度実行します。
CREATE INDEX partition_range_idx_local ON partition_range(id); EXPLAIN (COSTS OFF) SELECT * FROM partition_range WHERE id = 1;結果から、ローカルインデックスは各サブパーティションに作成されるため、すべてのサブパーティションのローカルインデックスが依然としてスキャンされていることがわかります。
QUERY PLAN -------------------------------------------------------------------------------------------------- Append -> Index Scan using partition_range_part01_id_idx on partition_range_part01 partition_range_1 Index Cond: (id = 1) -> Index Scan using partition_range_part02_id_idx on partition_range_part02 partition_range_2 Index Cond: (id = 1) -> Index Scan using partition_range_part03_id_idx on partition_range_part03 partition_range_3 Index Cond: (id = 1) (7 rows)GLOBALキーワードを使用してパーティションテーブルにグローバルインデックスを作成し、クエリを再度実行します。CREATE INDEX partition_range_idx_global ON partition_range(id) GLOBAL; EXPLAIN (COSTS OFF) SELECT * FROM partition_range WHERE id = 1;結果から、データベースがグローバルインデックスを使用して、データを含むサブパーティションを直接見つけていることがわかります。
QUERY PLAN ----------------------------------------------------------------------- Global Index Scan using partition_range_idx_global on partition_range Index Cond: (id = 1) (2 rows)
非パーティションキーの一意性制約
前の例と同じパーティションテーブルを使用します。パーティションキーは created_date ですが、一意性制約が必要な列は id です。
CREATE UNIQUE INDEX partition_range_id_unique_idx ON partition_range(id);
ERROR: unique constraint on partitioned table must include all partitioning columns
DETAIL: UNIQUE constraint on table "partition_range" lacks column "created_date" which is part of the partition key.ネイティブの PostgreSQL では、ローカルインデックスを使用して非パーティションキーに一意性制約を作成しようとすると、エラーが発生します。エラーメッセージは、パーティションキーがインデックスに含まれている必要があることを示しています。PolarDB for PostgreSQL および は、グローバルインデックス機能を強化しています。デフォルトでは、非パーティションキーの一意性制約は、グローバルインデックスに基づくグローバル制約に変換されます。この動作は polar_pk_in_non_partition_column_mode パラメーターによって制御されます。そのデフォルト値は global_index です。値を none に変更すると、動作はネイティブの PostgreSQL と同じになります。
ただし、以下に示すように、グローバルインデックスに一意性制約を追加する場合、この制限は適用されません。
CREATE UNIQUE INDEX partition_range_id_unique_idx ON partition_range(id) GLOBAL;パフォーマンステスト
pgbench を使用してスケールファクター 80000 のデータを生成し、パーティションテーブルと非パーティションテーブルを作成できます。
非パーティションキーでのポイントクエリのパフォーマンス
カテゴリ | TPS | |||||
プリペアドステートメント | 未使用 | 使用 | ||||
同時実行数 | 1 | 32 | 64 | 1 | 32 | 64 |
標準テーブル | 27,732 | 494,433 | 430,848 | 53,935 | 985,880 | 886,882 |
パーティションテーブル + ローカルインデックス | 367 | 4,155 | 3,688 | 856 | 8,742 | 6,790 |
パーティションテーブル + グローバルインデックス | 19,006 | 308,128 | 262,941 | 45,090 | 820,924 | 731,557 |
非パーティションキーでの TPC-B パフォーマンス
これには、ポイントクエリとデータ操作言語 (DML) 文が含まれます。
カテゴリ | TPS | |||||
プリペアドステートメント | 未使用 | 使用 | ||||
同時実行数 | 1 | 32 | 64 | 1 | 32 | 64 |
標準テーブル | 1,115 | 51,025 | 60,409 | 4,822 | 90,312 | 100,802 |
パーティションテーブル + ローカルインデックス | 271 | 2,903 | 2,524 | 550 | 5,276 | 4,237 |
パーティションテーブル + グローバルインデックス | 非対応 | 4,334 | 69,040 | 75,232 | ||
結論
グローバルインデックスを使用すると、パーティションテーブルに対するポイントクエリと DML 文のパフォーマンスを桁違いに向上させることができます。