すべてのプロダクト
Search
ドキュメントセンター

PolarDB:外部キー

最終更新日:May 28, 2024

このトピックでは、外部キーの使用方法について説明します。

重要

パーティション化されたテーブルに対する外部キー制約のチェックおよび維持の実装は、スタンドアロンのデータベースの実装よりも複雑であるため、不当な外部キーは、高性能のオーバーヘッドおよび著しいシステムスループットの低下をもたらす可能性がある。 パフォーマンス重視のシナリオでデータを使用する前に、データの完全検証と包括的なテストを実行することをお勧めします。

外部キーの作成

外部キー機能を有効にする

次のパラメーターを使用して、外部キー機能を有効または無効にできます。

SET [グローバル] enable_foreign_key = [true | false]

構文

[CONSTRAINT [シンボル]] 外国キー
    [index_name] (col_name, ...)
    参考文献tbl_name (col_name,...)
    [DELETE reference_option]
    [更新参照_オプション]

reference_option:
    RESTRICT | CASCADE | SET NULL | アクションなし | SET DEFAULT 

名前付け

外部キーは、次の命名規則に準拠している必要があります。

  • CONSTRAINTステートメントで名前が指定されている場合は、その名前が使用されます。

  • CONSTRAINTステートメントで名前を指定しない場合は、名前が自動的に生成されて使用されます。

  • index_nameパラメーターは、外部キーとともに作成されるインデックスの名前としてのみ使用され、外部キー名ではありません。

  • 外部キー名は、現在のテーブルで一意である必要があります。

サポートされているデータベースタイプ

外部キーは、AUTOモードとDRDSモードの両方でデータベース用に作成できます。 外部キーは、AUTOデータベースとDRDSデータベースの間で作成することもできます。

制限

  • InnoDBを使用するデータベースでのみ外部キーを作成できます。

  • 外部キーの列は、親テーブルの列と同じデータ型である必要があります。 それらのサイズ、精度、長さ、文字セット、および照合順序は同じでなければなりません。

  • 外部キーの列は、親テーブルの列に対応している必要があります。 外部キーと親テーブルに同じ数の列を作成する必要があります。

  • 外部キー名、インデックス名、テーブル名、および列名の長さは最大64文字です。

  • 外部キーの列は、親テーブルの列と同じインデックスを使用する必要があります。 インデックスの列は、外部キーの列と同じ順序である必要があります。 これにより、テーブル全体をスキャンする代わりに、外部キー制約のチェックにインデックスを使用できるようになります。

    子テーブルに外部キーインデックスがない場合、インデックスが自動的に作成されます。 インデックス名はカスタマイズすることも、外部キー名と同じにすることもできます。

  • プレフィックスインデックスはサポートされていません。 したがって、BLOBおよびTEXTデータ型の列に外部キーを作成することはできません。

  • 参照メソッドがSET NULLの外部キーの参照列はNOT NULL (主キー) にすることはできません。

  • 参照メソッドがSET DEFAULTである外部キーは作成できません。

MySQLとは異なる制限

生成された列 (stored、virtual、およびlogical) に外部キーを作成することはできません。

参照アクション

UPDATEまたはDELETE操作が、子テーブルの行が一致する親テーブルのキー値に影響を与える場合、結果はFOREIGN key句のon UPDATEおよびON DELETEサブ句で指定された参照アクションによって異なります。 参照アクションは次のとおりです。

パラメーター

の説明

DELETE NOアクション /

更新でアクションなし

親テーブルの削除または更新操作は、関連する外部キー値が参照テーブルに存在する場合、直ちに拒否されます。 これはデフォルトのアクションです。

デリート制限について /

更新制限について

ON DELETE NO ACTIONまたはON UPDATE NO ACTIONのエイリアス。

DELETE CASCADE /

CASCADEの更新

関連する外部キー値が参照テーブルに存在する場合、親テーブルから行を削除または更新し、子テーブルの一致する行を自動的に削除または更新します。

ON DELETE SET NULL /

ON UPDATE SET NULL

関連する外部キー値が参照テーブルに存在する場合、親テーブルから行を削除または更新し、子テーブルの外部キー列をNULLに設定します。 子テーブルの列がNULLでない場合、更新操作は失敗します。

DELETE SET DEFAULT /

更新セットのデフォルトについて

このアクションはまもなくサポートされます。

外部キー制約の例 (AUTOモードの場合)

このトピックのすべての例は、AUTOモードの外部キー機能を示しています。

次の例では、親テーブルと子テーブルの1つの列に外部キーを作成します。

CREATE TABLEの親 (
    id INT NOT NULL,
    主要なキー (id)
);

テーブルの子を作成する (
    id INT,
    parent_id INT、
    INDEX par_ind (parent_id) 、
    FOREIGN KEY (parent_id) DELETE CASCADEの親 (id) の参照
); 

次の例では、product_orderテーブルに2つの外部キーを作成します。 最初の外部キーは、製品テーブルの2列インデックスを参照します。 2番目の外部キーは、顧客テーブルの単一列インデックスを参照します。

テーブルプロダクトを作成する (
    カテゴリINT NOT NULL、id INT NOT NULL、
    デシマル価格、
    主要なキー (category, id)
);

テーブルの顧客を作成する (
    id INT NOT NULL,
    主要なキー (id)
);

CREATE TABLE product_order (
    INT NOT NULLなしAUTO_INCREMENT、
    product_category INT NOT NULL,
    product_id INT NOT NULL,
    customer_id INT NOT NULL,

    主要なキー (いいえ) 、
    インデックス (product_category, product_id),
    インデックス (customer_id) 、

    FOREIGN KEY (product_category, product_id)
      REFERENCES製品 (カテゴリ、id)
      削除制限のキャスケードの更新について、

    外国キー (customer_id)
      REFERENCES customer(id)
); 

外部キー制約の作成

次のALTER tableステートメントを使用して、既存のテーブルに外部キー制約を作成できます。

ALTER TABLE tbl_name
    追加 [CONSTRAINT [シンボル]] 外国キー
    [index_name] (col_name, ...)
    参考文献tbl_name (col_name,...)
    [DELETE reference_option]
    [更新参照_オプション] 

外部キーは、同じテーブルのインデックスを参照できます。 ALTER TABLEステートメントを実行してテーブルに外部キー制約を作成する場合は、まず外部キーによって参照される列にインデックスを作成する必要があります。

外部キー制約の削除

次のALTER tableステートメントを使用して、既存のテーブルから外部キー制約を削除できます。

ALTER TABLE tbl_name DROP FOREIGN KEY fk_symbol;

外部キー制約を作成するときに外部キー名を指定した場合、指定した名前を使用して外部キー制約を削除できます。 それ以外の場合は、自動的に生成された名前を使用してのみ外部キー制約を削除できます。 SHOW CREATE TABLEステートメントを実行すると、外部キー名を表示できます。

SHOW CREATE TABLE child\G
*************************** 1。 行 ***************************
       テーブル: 子供
テーブルの作成: Create Table 'child' (
'id' int(11) DEFAULT NULL、'parent_id' int (11) DEFAULT NULL、CONSTRAINT 'childr_ibfk_1 'FOREIGN KEY ('parent_id') REFERENCES 'parent' ('id') ON DELETE CASCADE ON UPDATE NO ACTION、インデックス 'par_ind' ('parent_id')
) エンジン=InnoDBデフォルト料金=utf8mb4 

外部キーチェック

PolarDB-Xは外部キーチェックをサポートしています。 foreign_key_checksパラメーターを使用して、デフォルトで有効になっている外部キーチェック機能を無効にできます。 外部キーチェック機能を有効にすると、通常、外部キー参照の整合性が保証されます。

foreign_key_checksパラメーターは、グローバルレベルまたはセッションレベルで有効になります。

外部キーチェック機能を無効にすると、次のような場合に便利です。

  • 外部キーによって参照される親テーブルを削除する前に、外部キーチェック機能を無効にする必要があります。

  • データベースにデータをインポートすると、外部キーと異なる順序でテーブルが作成されると、エラーが発生します。 テーブルを作成する前に、外部キーチェック機能を無効にする必要があります。 さらに、外部キーチェック機能を無効にすると、データをインポートするときのデータのインポートも高速化されます。

  • データベースにデータをインポートすると、子テーブルにインポートされたデータに対してエラーが報告されます。 データを子テーブルにインポートする前に、外部キーチェック機能を無効にする必要があります。

  • 外部キーに関するALTER TABLEステートメントを実行する前に、外部キーチェック機能を無効にする必要があります。

外部キーチェック機能を無効にすると、次の例外を除いて外部キーチェックは無視されます。

  • テーブルを再構築すると、外部キーの定義が正しくない場合でも、エラーが報告されます。

  • 外部キーに必要なインデックスを削除する場合は、最初に外部キーを削除する必要があります。 外部キーに必要なインデックスを削除すると、エラーが報告されます。

  • 外部キーの作成時に外部キーの条件または制限が満たされない場合でも、エラーが報告されます。

  • データベース間外部キーによって参照される親テーブルが存在するデータベースを削除すると、エラーが報告されます。

外部キーチェック機能を無効にすると、次のような意味があります。

  • テーブルに他のテーブルを参照し、他のテーブルの外部キーによって参照される外部キーが含まれている場合でも、削除できます。

  • 外部キーチェック機能を有効にしても、テーブル内のデータはスキャンされません。 したがって、外部キーチェック機能を再度有効にすると、外部キーチェック機能が無効になっているときにテーブルに追加されたデータの整合性がチェックされません。

ロック

INSERTまたはUPDATEステートメントが子テーブルで実行されると、外部キー制約は、対応する外部キー値が親テーブルに存在するかどうかをチェックし、トランザクションを開始し、親テーブルの行をロックします。 これは、外部キー制約を破壊し得る他の操作によって外部キー値が修正されることを防止する。 このロックは、親テーブルの外部キー値が配置されている行に対してSELECT FOR UPDATEステートメントを実行することと同じです。 データが子テーブルに同時に書き込まれるシナリオでは、参照される外部キー値のほとんどが同じである場合、深刻なロック競合が発生する可能性があります。

外部キー定義とメタデータ

外部キー定義を表示するには、SHOW CREATE TABLEまたはSHOW FULL CREATE TABLE (論理外部キーまたは物理外部キーの確認) ステートメントを実行します。

SHOW CREATE TABLE child\G
*************************** 1。 行 ***************************
       テーブル: 子供
テーブルの作成: Create Table 'child' (
'id' int(11) DEFAULT NULL、'parent_id' int (11) DEFAULT NULL、CONSTRAINT 'childr_ibfk_1 'FOREIGN KEY ('parent_id') REFERENCES 'parent' ('id') ON DELETE CASCADE ON UPDATE NO ACTION、インデックス 'par_ind' ('parent_id')
) エンジン=InnoDBデフォルト料金=utf8mb4

フル作成テーブルの子 \Gを表示
*************************** 1。 行 ***************************
       テーブル: 子供
テーブルの作成: PARTITION Table 'child '(
'id' int(11) DEFAULT NULL、'parent_id' int (11) DEFAULT NULL、'_drds_implicit_id_' bigint(20) NOT NULL AUTO_INCREMENT、PRIMARY KEY ('_drds_implicit_id_') 、CONSTRAINT 'childr_ibfk_1 'FOREIGN KEY ('parent_id') REFERENCES 'parent' ('id') ON DELETE CASCADE ON UPDATE NO ACTION /* TYPE LOGICAL * /、グローバルインデックス /* par_ind_$871c */ 'par_ind' ('parent_id')
PARTITION BY KEY('parent_id','_drds_implicit_id_')
パーティー3,
ローカルキー '_local_par_ind' ('parent_id')
) エンジン=InnoDBデフォルト料金=utf8mb4
PARTITION BY KEY('_drds_implicit_id_')
パーティー3
/* tablegroup = 'tg1121' */ 

外部キーに関する情報は、次のシステムテーブルからも取得できます。

  • INFORMATION_SCHEMA.TABLE_CONSTRAINTS

  • INFORMATION_SCHEMA.KEY_COLUMN_USAGE

  • INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS

  • INFORMATION_SCHEMA.INNODB_SYS_FOREIGN

  • INFORMATION_SCHEMA.INNODB_SYS_FOREIGN_COLS

SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE REFERENCED_TABLE_SCHEMA = 'test';
+ ------------ ---------------------
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | CONSTRAINT_NAME |
+ ------------ ---------------------
| test | product_order | customer_id | product_order_ibfk_2 |
| test | child | parent_id | child_ibfk_1 |
| test | product_order | product_category | product_order_ibfk_1 |
| test | product_order | product_id | product_order_ibfk_1 |
+ ------------ ---------------------

CONSTRAINT_TYPE='FOREIGN KEY' およびCONSTRAINT_SCHEMA = 'test'\Gから * を選択します。*************************** 1。 行 ***************************
CONSTRAINT_CATALOG: def
 CONSTRAINT_SCHEMA: テスト
   CONSTRAINT_NAME: child_ibfk_1
      TABLE_SCHEMA: テスト
        TABLE_NAME: 子
   CONSTRAINT_TYPE: 外国キー
          ENFORCED: はい

SELECT * FROM CONSTRAINT_SCHEMA = 'test'\G
*************************** 1。 行 ***************************
       CONSTRAINT_CATALOG: def
        CONSTRAINT_SCHEMA: テスト
          CONSTRAINT_NAME: child_ibfk_1
UNIQUE_CONSTRAINT_CATALOG: def
 UNIQUE_CONSTRAINT_SCHEMA: テスト
   UNIQUE_CONSTRAINT_NAME: プライマリー
             MATCH_OPTION: なし
              UPDATE_RULE: アクションなし
              DELETE_RULE: カスケード
               TABLE_NAME: 子
    REFERENCED_TABLE_NAME: 親
    
SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN WHERE ID = 'test/child_ibfk_1 ';
+ ------------------- + -------------------------------------------------- +
| ID | FOR_NAME | REF_NAME | N_COLS | タイプ |
+ ------------------- + -------------------------------------------------- +
| test/child_ibfk_1 | test/child | test/parent | 1 | 33 |
+ ------------------- + -------------------------------------------------- +

SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN_COLS WHERE ID = 'test/child_ibfk_1 ';
+ ------------------- ------------ --------------------------- +
| ID | FOR_COL_NAME | REF_COL_NAME | POS |
+ ------------------- ------------ --------------------------- +
| test/child_ibfk_1 | parent_id | id | 0 |
+ ------------------- ------------ ------------------------------- + 

外部キーカスケードの実行計画を表示する

EXPLAINステートメントを実行すると、外部キーのカスケードに関与する可能性のある実行計画を表示できます。 外部キーフィールドは、チェックされる外部キーを示す。

EXPLAIN DELETE FROM親WHERE id = 1;
+ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +
| ローカル実行 |
+ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +
| LogicalModify(TYPE="DELETE" 、TABLES="test.parent") |
| LogicalView(tables="parent[p3]", sql="SELECT 'id' FROM 'parent' AS 'parent' WHERE ('id' = ?) FOR UPDATE") |
| >> 外部キー: test.child.child_ibfk_1 |
| LogicalModify(TYPE="DELETE" 、TABLES="test.child") |
| 収集 (同時=true) |
| LogicalView(tables="child[p1,p2,p3]", shardCount=3, sql="SELECT 'id', 'parent_id' FROM 'child' AS 'child' WHERE ('parent_id' = ?) FOR UPDATE") |
| >>>> 外部キー: test.grand_child.grand_child_ibfk_1 |
| LogicalModify(TYPE="DELETE" 、TABLES="test.grand_child") |
| 収集 (同時=true) |
| LogicalView(tables="grand_child[p1,p2,p3]", shardCount=3, sql="SELECT 'id', 'parent_id' FROM 'grand_child' AS 'grand_child' WHERE ('parent_id' = ?) FOR UPDATE") |
| HitCache:true |
| ソース: PLAN_CACHE |
| TemplateId: 78fcce0f |
+ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 

EXPLAIN COSTステートメントを実行して、コストを見積もり、複雑なカスケード操作によって引き起こされる高パフォーマンスのオーバーヘッドを回避することもできます。

詳細については、「EXPLAIN」をご参照ください。

EXPLAIN COST DELETE FROM parent WHERE id = 1;
+ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +
| ローカル実行 |
+ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +
| LogicalModify(TYPE="DELETE" 、TABLES="test.parent"): rowcount = 1.0、累積コスト=値=1.0005002E7、cpu = 2.0、メモリ=0.0、io = 1.0、net = 2.0 |
| LogicalView(tables="parent[p3]" 、sql="SELECT 'id' FROM 'parent' AS 'parent' WHERE ('id' = ?) FOR UPDATE"): rowcount = 1.0、累積コスト=値=5005001.0、cpu = 1.0、メモリ=0.0、io = 1.0、net = 1.0 |
| >> 外部キー: test.child.child_ibfk_1 |
| LogicalModify(TYPE="DELETE" 、TABLES="test.child"): rowcount = 1.0、累積コスト=値=1.2505003E7、cpu = 3.0、メモリ=0.0、io = 1.0、net = 2.5 |
| 収集 (同時=true): rowcount = 1.0、累積コスト=値=7505002.0、cpu = 2.0、メモリ=0.0、io = 1.0、net = 1.5 |
| LogicalView(tables="child[p1,p2,p3]", shardCount=3, sql="SELECT 'id', 'parent_id' FROM 'child 'AS 'child' WHERE ('parent_id' = ?) FOR UPDATE"): rowcount = 1.0, 累積コスト=値=7505001.0, cpu = 1.0, memory = 0.0, io = 1.0 | net 1.5
| >>>> 外部キー: test.grand_child.grand_child_ibfk_1 |
| LogicalModify(TYPE="DELETE" 、TABLES="test.grand_child"): rowcount = 1.0、累積コスト=value = 1.2505003E7、cpu = 3.0、memory = 0.0、io = 1.0、net = 2.5 |
| 収集 (同時=true): rowcount = 1.0、累積コスト=値=7505002.0、cpu = 2.0、メモリ=0.0、io = 1.0、net = 1.5 |
| LogicalView(tables="grand_child[p1,p2,p3]" 、shardCount=3、sql="SELECT 'id' 、'parent_id' FROM 'grand_child' WHERE ('parent_id' = ?) FOR UPDATE"): rowcount = 1.0、累積コスト=値=7505001.0、cpu=0.0 |=1.0、net
| HitCache:true |
| ソース: PLAN_CACHE |
| WorkloadType: TP |
| TemplateId: 78fcce0f |
+ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 

互換性

外部キーチェックとプッシュダウン

外部キーを作成するとき、PolarDB-Xは、テーブル構造に基づいて外部キーを押下するかどうかを決定します。 プッシュダウンされた外部キーは物理外部キーと呼ばれ、プッシュダウンされていない外部キーは論理外部キーと呼ばれます。 外部キーを押すとパフォーマンスが向上します。

外部キーチェック機能を無効にした後に親テーブルを削除して再構築するか、親テーブル構造が不明のままで外部キーを含む子テーブルを最初に作成する場合は、論理外部キーがデフォルトで作成されます。

MySQLとの互換性

マッチング方法

MySQLでは、外部キー制約は、MATCH SIMPLE、MATCH FULL、MATCH PARTIALの3つのマッチング方法をサポートしています。 MATCH SIMPLEはデフォルトで使用されます。 MATCH PARTIALは後でサポートされます。 PolarDB-Xは、デフォルトのMATCH SIMPLEメソッドをサポートしています。 マッチング方法は変更できません。

内部参加参照

MySQLとPolarDB-Xはどちらも解析できますが、内部参加REFERENCESによって定義された外部キーは無視できます。 REFERENCESパラメーターは、外部キー定義の一部である場合にのみチェックおよび実行されます。 次の例では、外部キー制約が定義されている場合にのみREFERENCESを使用します。

CREATE TABLEの親 (
    id INTキー
);

テーブルの子を作成する (
    id INT,
    pid INT REFERENCES親 (id)
);

ショー作成テーブルの子; 

結果は、子テーブルに外部キーが含まれていないことを示します。

SHOW CREATE TABLE child\G
*************************** 1。 行 ***************************
       テーブル: 子供
テーブルの作成: Create Table 'child' (
'id' int(11) DEFAULT NULL、'pid' int (11) DEFAULT NULL
) エンジン=InnoDBデフォルト料金=utf8mb4