📣
TiDB Cloud Premium はパブリックプレビュー中です。エンタープライズワークロード向けの無制限のスケーリング、即時の弾力性、高度なセキュリティを提供します。このページは自動翻訳されたものです。原文はこちらからご覧ください。

CREATE INDEX



このステートメントは、既存のテーブルに新しいインデックスを追加します。これは、 ALTER TABLE .. ADD INDEXの代替構文であり、MySQL との互換性のために含まれています。

概要

CreateIndexStmt
CREATEIndexKeyTypeOptINDEXIfNotExistsIdentifierIndexTypeOptONTableName(IndexPartSpecificationList)IndexOptionListIndexLockAndAlgorithmOpt
IndexKeyTypeOpt
UNIQUESPATIALFULLTEXT
IfNotExists
IFNOTEXISTS
IndexTypeOpt
IndexType
IndexPartSpecificationList
IndexPartSpecification,
IndexOptionList
IndexOption
IndexLockAndAlgorithmOpt
LockClauseAlgorithmClauseAlgorithmClauseLockClause
IndexType
USINGTYPEIndexTypeName
IndexPartSpecification
ColumnNameOptFieldLen(Expression)Order
IndexOption
KEY_BLOCK_SIZE=LengthNumIndexTypeWITHPARSERIdentifierCOMMENTstringLitVISIBLEINVISIBLEGLOBALLOCALWHEREExpression
IndexTypeName
BTREEHASHRTREE
ColumnName
Identifier.Identifier.Identifier
OptFieldLen
FieldLen
IndexNameList
IdentifierPRIMARY,IdentifierPRIMARY
KeyOrIndex
KeyIndex

mysql> CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, c1 INT NOT NULL); Query OK, 0 rows affected (0.10 sec) mysql> INSERT INTO t1 (c1) VALUES (1),(2),(3),(4),(5); Query OK, 5 rows affected (0.02 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> EXPLAIN SELECT * FROM t1 WHERE c1 = 3; +-------------------------+----------+-----------+---------------+--------------------------------+ | id | estRows | task | access object | operator info | +-------------------------+----------+-----------+---------------+--------------------------------+ | TableReader_7 | 10.00 | root | | data:Selection_6 | | └─Selection_6 | 10.00 | cop[tikv] | | eq(test.t1.c1, 3) | | └─TableFullScan_5 | 10000.00 | cop[tikv] | table:t1 | keep order:false, stats:pseudo | +-------------------------+----------+-----------+---------------+--------------------------------+ 3 rows in set (0.00 sec) mysql> CREATE INDEX c1 ON t1 (c1); Query OK, 0 rows affected (0.30 sec) mysql> EXPLAIN SELECT * FROM t1 WHERE c1 = 3; +------------------------+---------+-----------+------------------------+---------------------------------------------+ | id | estRows | task | access object | operator info | +------------------------+---------+-----------+------------------------+---------------------------------------------+ | IndexReader_6 | 10.00 | root | | index:IndexRangeScan_5 | | └─IndexRangeScan_5 | 10.00 | cop[tikv] | table:t1, index:c1(c1) | range:[3,3], keep order:false, stats:pseudo | +------------------------+---------+-----------+------------------------+---------------------------------------------+ 2 rows in set (0.00 sec) mysql> ALTER TABLE t1 DROP INDEX c1; Query OK, 0 rows affected (0.30 sec) mysql> CREATE UNIQUE INDEX c1 ON t1 (c1); Query OK, 0 rows affected (0.31 sec)

式インデックス or 関数インデックス

クエリのフィルタリング条件が特定の式に基づいている場合、通常のインデックスが効果を発揮せず、テーブル全体をスキャンしてクエリを実行するしかないため、クエリのパフォーマンスは比較的低くなります。式インデックスは、式に基づいて作成できる特殊なインデックスです。式インデックスが作成されると、TiDB はそのインデックスを式ベースのクエリに使用できるようになり、クエリのパフォーマンスが大幅に向上します。

例えば、 LOWER(col1)に基づいてインデックスを作成する場合は、次の SQL ステートメントを実行します。

CREATE INDEX idx1 ON t1 ((LOWER(col1)));

または、以下の同等のステートメントを実行することもできます。

ALTER TABLE t1 ADD INDEX idx1((LOWER(col1)));

テーブルを作成する際に、式インデックスを指定することもできます。

CREATE TABLE t1 ( col1 CHAR(10), col2 CHAR(10), INDEX ((LOWER(col1))) );

式インデックスを削除する方法は、通常のインデックスを削除する方法と同じです。

DROP INDEX idx1 ON t1;

式インデックス or 関数インデックスには、さまざまな種類の式が含まれます。正確性を確保するため、式インデックスの作成には、完全にテストされた一部の関数のみが許可されています。つまり、本番環境では、これらの関数のみが式で使用できます。これらの関数は、 tidb_allow_function_for_expression_index変数を照会することで取得できます。現在、許可されている関数は以下のとおりです。

上記のリストに含まれていない関数は、十分にテストされておらず、本番環境での関数は推奨されません。これらは実験的とみなされます。演算子、 CASTCASE WHENなどの他の式も実験的とみなされ、本番環境での本番は推奨されません。

それでもこれらの式を使用したい場合は、 TiDB設定ファイルで次の構成を行うことができます。

allow-expression-index = true

クエリ文の式が式インデックスの式と一致する場合、オプティマイザはクエリに対して式インデックスを選択できます。ただし、統計情報によっては、オプティマイザが式インデックスを選択しない場合もあります。このような場合は、オプティマイザヒントを使用することで、オプティマイザに式インデックスを選択させることができます。

以下の例では、式idx LOWER(col1) } を作成するとします。

クエリ文の結果が同じ式である場合、式インデックスが適用されます。次の文を例にとります。

SELECT LOWER(col1) FROM t;

フィルタリング条件に同じ式が含まれている場合、式のインデックスが適用されます。以下のステートメントを例に挙げます。

SELECT * FROM t WHERE LOWER(col1) = "a"; SELECT * FROM t WHERE LOWER(col1) > "a"; SELECT * FROM t WHERE LOWER(col1) BETWEEN "a" AND "b"; SELECT * FROM t WHERE LOWER(col1) IN ("a", "b"); SELECT * FROM t WHERE LOWER(col1) > "a" AND LOWER(col1) < "b"; SELECT * FROM t WHERE LOWER(col1) > "b" OR LOWER(col1) < "a";

クエリが同じ式でソートされている場合、式インデックスが適用されます。次のステートメントを例に挙げます。

SELECT * FROM t ORDER BY LOWER(col1);

同じ式が集約関数( GROUP BY )に含まれている場合、式のインデックスが適用されます。次のステートメントを例にとります。

SELECT MAX(LOWER(col1)) FROM t; SELECT MIN(col1) FROM t GROUP BY LOWER(col1);

式インデックスに対応する式を確認するには、 SHOW INDEXを実行するか、システム テーブルinformation_schema.tidb_indexesおよびテーブルinformation_schema.STATISTICSを確認してください。出力のExpression列は、対応する式を示します。式インデックス以外の場合は、 NULLが表示されます。

式インデックスの維持コストは、他のインデックスの維持コストよりも高くなります。これは、行が挿入または更新されるたびに式の値を計算する必要があるためです。式の値は既にインデックスに格納されているため、オプティマイザが式インデックスを選択する際に、この値を再計算する必要はありません。

したがって、クエリのパフォーマンスが挿入および更新のパフォーマンスを上回る場合は、式にインデックスを作成することを検討できます。

式インデックスには、MySQL と同じ構文と制限があります。これらは、生成された非表示の仮想列にインデックスを作成することによって実装されるため、サポートされる式はすべての仮想生成列の制限制限を継承します。

多値インデックス

多値インデックスは、配列列に定義されるセカンダリインデックスの一種です。通常のインデックスでは、1つのインデックスレコードが1つのデータレコードに対応します(1:1)。多値インデックスでは、複数のインデックスレコードが1つのデータレコードに対応します(N:1)。多値インデックスは、JSON配列のインデックス付けに使用されます。たとえば、 zipcodeフィールドに定義された多値インデックスはzipcode配列の各要素に対して1つのインデックスレコードを生成します。

{ "user":"Bob", "user_id":31, "zipcode":[94477,94536] }

多値インデックスを作成する

式インデックスを作成するのと同様に、インデックス定義でCAST(... AS ... ARRAY)関数を使用することで、多値インデックスを作成できます。

mysql> CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, name CHAR(10), custinfo JSON, INDEX zips((CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY))) );

多値インデックスを一意インデックスとして定義することができます。

mysql> CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, name CHAR(10), custinfo JSON, UNIQUE INDEX zips( (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY))) );

多値インデックスを一意インデックスとして定義した場合、重複データを挿入しようとするとエラーが報告されます。

mysql> INSERT INTO customers VALUES (1, 'pingcap', '{"zipcode": [1,2]}'); Query OK, 1 row affected (0.01 sec) mysql> INSERT INTO customers VALUES (1, 'pingcap', '{"zipcode": [2,3]}'); ERROR 1062 (23000): Duplicate entry '2' for key 'customers.zips'

同じレコード内に重複する値が存在することは許容されるが、異なるレコード内に重複する値が存在する場合はエラーが報告される。

-- Insert succeeded mysql> INSERT INTO t1 VALUES('[1,1,2]'); mysql> INSERT INTO t1 VALUES('[3,3,3,4,4,4]'); -- Insert failed mysql> INSERT INTO t1 VALUES('[1,2]'); mysql> INSERT INTO t1 VALUES('[2,3]');

多値インデックスを複合インデックスとして定義することもできます。

mysql> CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, name CHAR(10), custinfo JSON, INDEX zips(name, (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY))) );

多値インデックスが複合インデックスとして定義される場合、多値部分は任意の位置に現れることができますが、一度しか現れません。

mysql> CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, name CHAR(10), custinfo JSON, INDEX zips(name, (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)), (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY))) ); ERROR 1235 (42000): This version of TiDB doesn't yet support 'more than one multi-valued key part per index'.

書き込まれるデータは、多値インデックスで定義された型と完全に一致する必要があります。一致しない場合、データの書き込みは失敗します。

-- All elements in the zipcode field must be the UNSIGNED type. mysql> INSERT INTO customers VALUES (1, 'pingcap', '{"zipcode": [-1]}'); ERROR 3752 (HY000): Value is out of range for expression index 'zips' at row 1 mysql> INSERT INTO customers VALUES (1, 'pingcap', '{"zipcode": ["1"]}'); -- Incompatible with MySQL ERROR 3903 (HY000): Invalid JSON value for CAST for expression index 'zips' mysql> INSERT INTO customers VALUES (1, 'pingcap', '{"zipcode": [1]}'); Query OK, 1 row affected (0.00 sec)

多値インデックスを使用する

詳細については、インデックスの選択 - 多値インデックスを使用するを参照してください。

制限事項

  • 空のJSON配列の場合、対応するインデックスレコードは生成されません。
  • CAST(... AS ... ARRAY)のターゲットタイプはBINARYJSONYEARFLOAT 、およびDECIMALいずれにもなりません。ソースタイプは JSON である必要があります。
  • 多値インデックスをソートに使用することはできません。
  • JSON配列に対してのみ、多値インデックスを作成できます。
  • 多値インデックスは、主キーまたは外部キーとして使用することはできません。
  • 多値インデックスが使用する追加のストレージ領域は、1行あたりの配列要素の平均数×通常のセカンダリインデックスが使用する領域に等しくなります。
  • 通常のインデックスと比較して、多値インデックスではDML操作によって変更されるインデックスレコードの数が多くなるため、多値インデックスは通常のインデックスよりもパフォーマンスに大きな影響を与えます。
  • 多値インデックスは式インデックスの特殊なタイプであるため、式インデックスと同様の制限があります。
  • テーブルが多値インデックスを使用している場合、 BR、TiCDC、またはTiDB Lightningを使用して、v6.6.0より前のTiDBクラスタにテーブルをバックアップ、レプリケート、またはインポートすることはできません。
  • 複雑な条件を含むクエリの場合、TiDB は多値インデックスを選択できない場合があります。多値インデックスでサポートされる条件パターンについては、 多値インデックスを使用するを参照してください。

Partial indexes v8.5.7 の新機能

部分インデックスは、テーブル内の行のサブセットに対して構築されるインデックスです。部分インデックスを作成する際には、その行のサブセットを定義するために、述語とも呼ばれる条件式を指定できます。インデックスには、その述語を満たす行に対するエントリのみが含まれます。

使用シナリオ

次のシナリオでは、部分インデックスを使用することで、クエリパフォーマンスの向上やインデックス管理のオーバーヘッド削減に役立ちます。

  • 選択的フィルタリング: 特定の条件に基づいて少数の行のサブセットを頻繁にクエリする場合、部分インデックスを使用できます。部分インデックスの述語を満たすクエリでは、TiDB は部分インデックスを使用して無関係な行のスキャンを回避し、インデックスが占有するストレージ容量を削減できます。
  • 条件付き一意性: 特定の条件を満たす行に対してのみ一意制約を適用する必要がある場合、ユニーク部分インデックスを使用して、テーブル全体に一意制約を適用することを回避できます。
  • DML オーバーヘッドの削減: 多くの INSERTUPDATE、または DELETE 操作がインデックス化する必要のない行に影響する場合、部分インデックスを使用できます。完全なインデックスを管理する場合と比べて、部分インデックスを管理することでインデックス管理のオーバーヘッドを削減できます。

部分インデックスの作成

インデックス定義に WHERE 句を追加することで、部分インデックスを作成できます。例:

CREATE TABLE t1 (c1 INT, c2 INT, c3 TEXT); CREATE INDEX idx1 ON t1 (c1) WHERE c2 > 10;

ALTER TABLE を使用して部分インデックスを作成することもできます。

ALTER TABLE t1 ADD INDEX idx2 (c1, c2) WHERE c3 = 'abc';

テーブル作成時に部分インデックスを指定することもできます。

CREATE TABLE t2 ( id INT PRIMARY KEY, status VARCHAR(20), created_at DATETIME, INDEX idx_active_status (status) WHERE status = 'active' );

使用例

次の例は、部分インデックスを効果的に使用する方法を示しています。

ユーザーデータを含むテーブルを作成します。

CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100), status VARCHAR(20), created_at DATETIME, score INT );

一般的なクエリパターンに対して部分インデックスを作成します。

CREATE INDEX idx_active_users ON users (name) WHERE status = 'active'; CREATE INDEX idx_high_score_users ON users (created_at) WHERE score > 1000; CREATE INDEX idx_pending_status ON users (status) WHERE status = 'pending';

次のクエリでは部分インデックスを使用できます。

mysql> EXPLAIN SELECT * FROM users WHERE status = 'active' AND name = 'John'; +-------------------------------+---------+-----------+-------------------------------------------+-------------------------------------------------------+ | id | estRows | task | access object | operator info | +-------------------------------+---------+-----------+-------------------------------------------+-------------------------------------------------------+ | IndexLookUp_9 | 1.00 | root | | | | ├─IndexRangeScan_6(Build) | 10.00 | cop[tikv] | table:users, index:idx_active_users(name) | range:["John","John"], keep order:false, stats:pseudo | | └─Selection_8(Probe) | 1.00 | cop[tikv] | | eq(test.users.status, "active") | | └─TableRowIDScan_7 | 10.00 | cop[tikv] | table:users | keep order:false, stats:pseudo | +-------------------------------+---------+-----------+-------------------------------------------+-------------------------------------------------------+ 4 rows in set (0.00 sec) mysql> EXPLAIN SELECT * FROM users WHERE status = 'active' ORDER BY name; +-------------------------------+----------+-----------+-------------------------------------------+---------------------------------+ | id | estRows | task | access object | operator info | +-------------------------------+----------+-----------+-------------------------------------------+---------------------------------+ | IndexLookUp_18 | 10.00 | root | | | | ├─IndexFullScan_15(Build) | 10000.00 | cop[tikv] | table:users, index:idx_active_users(name) | keep order:true, stats:pseudo | | └─Selection_17(Probe) | 10.00 | cop[tikv] | | eq(test.users.status, "active") | | └─TableRowIDScan_16 | 10000.00 | cop[tikv] | table:users | keep order:false, stats:pseudo | +-------------------------------+----------+-----------+-------------------------------------------+---------------------------------+ 4 rows in set (0.00 sec) mysql> EXPLAIN SELECT * FROM users WHERE score > 10000 ORDER BY created_at; +-------------------------------+----------+-----------+-----------------------------------------------------+--------------------------------+ | id | estRows | task | access object | operator info | +-------------------------------+----------+-----------+-----------------------------------------------------+--------------------------------+ | IndexLookUp_18 | 3333.33 | root | | | | ├─IndexFullScan_15(Build) | 10000.00 | cop[tikv] | table:users, index:idx_high_score_users(created_at) | keep order:true, stats:pseudo | | └─Selection_17(Probe) | 3333.33 | cop[tikv] | | gt(test.users.score, 10000) | | └─TableRowIDScan_16 | 10000.00 | cop[tikv] | table:users | keep order:false, stats:pseudo | +-------------------------------+----------+-----------+-----------------------------------------------------+--------------------------------+ 4 rows in set (0.00 sec) mysql> EXPLAIN SELECT * FROM users WHERE status = 'pending'; +-------------------------------+---------+-----------+-----------------------------------------------+-------------------------------------------------------------+ | id | estRows | task | access object | operator info | +-------------------------------+---------+-----------+-----------------------------------------------+-------------------------------------------------------------+ | IndexLookUp_7 | 10.00 | root | | | | ├─IndexRangeScan_5(Build) | 10.00 | cop[tikv] | table:users, index:idx_pending_status(status) | range:["pending","pending"], keep order:false, stats:pseudo | | └─TableRowIDScan_6(Probe) | 10.00 | cop[tikv] | table:users | keep order:false, stats:pseudo | +-------------------------------+---------+-----------+-----------------------------------------------+-------------------------------------------------------------+ 3 rows in set (0.00 sec)

クエリの述語が部分インデックスで定義された条件を満たさない場合、TiDB はヒントを指定していても部分インデックスを選択しません。たとえば、次のステートメントでは、クエリ述語 score > 100 が部分インデックス定義 score > 1000 を満たさないため、部分インデックス idx_high_score_users を使用できません。

mysql> EXPLAIN SELECT * FROM users USE INDEX(idx_high_score_users) WHERE score > 100 ORDER BY created_at; +---------------------------+----------+-----------+---------------+--------------------------------+ | id | estRows | task | access object | operator info | +---------------------------+----------+-----------+---------------+--------------------------------+ | Sort_5 | 3333.33 | root | | test.users.created_at | | └─TableReader_10 | 3333.33 | root | | data:Selection_9 | | └─Selection_9 | 3333.33 | cop[tikv] | | gt(test.users.score, 100) | | └─TableFullScan_8 | 10000.00 | cop[tikv] | table:users | keep order:false, stats:pseudo | +---------------------------+----------+-----------+---------------+--------------------------------+

制限事項

  • 部分インデックスの WHERE 句では、基本的な比較演算子(=!=<<=>>=)、IS NULLIS NOT NULL、および定数値を使用する IN 述語をサポートします。
  • 述語内のカラムと定数値は同じデータ型である必要があります。
  • 述語は同じテーブルのカラムのみを参照できます。
  • 式インデックスには部分インデックスを作成できません。

不可視インデックス

デフォルトでは、不可視インデックスとはクエリオプティマイザによって無視されるインデックスのことです。

CREATE TABLE t1 (c1 INT, c2 INT, UNIQUE(c2)); CREATE UNIQUE INDEX c1 ON t1 (c1) INVISIBLE;

TiDB v8.0.0以降では、システム変数tidb_opt_use_invisible_indexes変更することで、オプティマイザに不可視インデックスを選択させることができます。

詳細については、 ALTER INDEX参照してください。

関連するシステム変数

CREATE INDEXステートメントに関連付けられているシステム変数は、 tidb_ddl_enable_fast_reorgtidb_ddl_reorg_worker_cnttidb_ddl_reorg_batch_sizetidb_enable_auto_increment_in_generated 、およびtidb_ddl_reorg_priority 。 詳細システム変数を参照してください。

MySQLとの互換性

  • TiDB Self-Managed およびTiDB Cloud DedicatedはFULLTEXT構文の解析をサポートしていますが、 FULLTEXTHASH 、およびSPATIALインデックスの使用はサポートしていません。

  • TiDB は、MySQL との互換性のために、 HASHBTREERTREEなどのインデックス タイプを構文で受け入れますが、それらを無視します。

  • 降順インデックスはサポートされていません( MySQL 5.7と同様)。

  • CLUSTEREDタイプの主キーをテーブルに追加することはサポートされていません。 CLUSTEREDタイプの主キーの詳細については、クラスター化インデックスを参照してください。

  • 式インデックスはビューと互換性がありません。ビューを使用してクエリを実行する場合、式インデックスを同時に使用することはできません。

  • 式インデックスはバインディングとの互換性に問題があります。式インデックスの式に定数が含まれている場合、対応するクエリ用に作成されるバインディングのスコープが拡張されます。たとえば、式インデックスの式がa+1で、対応するクエリ条件がa+1 > 2であるとします。この場合、作成されるバインディングはa+? > ?となり、 a+2 > 2のような条件を持つクエリも式インデックスの使用を強制され、実行プランが最適化されません。さらに、これは SQL プラン管理 (SPM) におけるベースラインのキャプチャとベースラインの進化にも影響します。

  • 多値インデックスを使用して書き込まれるデータは、定義されたデータ型と正確に一致する必要があります。そうしないと、データの書き込みは失敗します。詳細については、 多値インデックスを作成する参照してください。

  • UNIQUE KEYインデックス オプションを使用して、 GLOBALグローバルインデックスとして設定することはパーティション化されたテーブルの TiDB 拡張機能であり、MySQL とは互換性がありません。

参照

このページは役に立ちましたか?