数据库内部结构:外键约束的实现
Database internals: implementation of a foreign key constraint
外键是如何实现的,例如,在 PostgreSQL 中?
我注意到在创建外键时涉及大量散列,因此我假设在引用主键列的外键列上创建基于散列的索引。如果是这样(例如,当我们想从引用的 table 中删除一行时——这个带有主键的行或所谓的主键 table)我们可以很容易地检查引用的行 table =14=]实际引用与否。更重要的是,DBMS 可能要求引用的主键列上至少有一个 B+ 树索引,因为当我们要向引用 table 插入新行时,我们可以很容易地检查是否有所需的行引用的 table 中存在主键值。一些消息来源声称触发器用于确保外键约束。
在 Postgres 中,master table 中的引用列需要具有 UNIQUE
或 PRIMARY KEY
约束。 Per documentation:
The referenced columns must be the columns of a non-deferrable unique
or primary key constraint in the referenced table.
两者目前都总是 使用 btree 索引实现。所以在引用的列上总是一个btree索引:
- How does PostgreSQL enforce the UNIQUE constraint / what type of index does it use?
引用列根本不需要索引。如果 master table 中的行永远不会更新或删除,这可能就足够了。否则,引用列也应该被索引,但这不是由系统强制执行的。这只是关于性能优化,而不是数据完整性。
FK 约束本身的实际实现是系统目录中的一个条目pg_constraint
, a special internal trigger and another entry in pg_depend
。
我检查过,确实,PostgreSQL 没有为外键创建索引(使用此查询:)。
另一方面,为外键创建了一些触发器:
test=# SELECT tgname AS trigger_name
FROM pg_trigger
WHERE tgname !~ '^pg_';
trigger_name
--------------
(0 rows)
test=# ALTER TABLE LINEITEM ADD CONSTRAINT LINEITEM_FK1 FOREIGN KEY (L_ORDERKEY) REFERENCES ORDERS;
ALTER TABLE
test=# SELECT tgname AS trigger_name
FROM pg_trigger
WHERE tgname !~ '^pg_';
trigger_name
------------------------------
RI_ConstraintTrigger_a_16419
RI_ConstraintTrigger_a_16420
RI_ConstraintTrigger_c_16421
RI_ConstraintTrigger_c_16422
因此,我假设在 PostgreSQL 中创建外键期间,为引用的 table 创建了一个哈希映射,然后对引用的每一行执行探测 table。
有趣的是,MonetDB 为主键和外键创建了不同类型的索引(可能分别是 join-index 和 hash-index)。
sql>select * from sys.idxs;
+------+----------+------+-------------+
| id | table_id | type | name |
+======+==========+======+=============+
| 6467 | 6446 | 0 | orders_pk |
| 6470 | 6464 | 1 | lineitem_fk |
+------+----------+------+-------------+
2 tuples (3.921ms)
此外,Oracle 使用索引强制执行主键约束,默认情况下它不会为外键创建任何索引,但是,有一些外键索引技巧:https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:292016138754
外键是如何实现的,例如,在 PostgreSQL 中?
我注意到在创建外键时涉及大量散列,因此我假设在引用主键列的外键列上创建基于散列的索引。如果是这样(例如,当我们想从引用的 table 中删除一行时——这个带有主键的行或所谓的主键 table)我们可以很容易地检查引用的行 table =14=]实际引用与否。更重要的是,DBMS 可能要求引用的主键列上至少有一个 B+ 树索引,因为当我们要向引用 table 插入新行时,我们可以很容易地检查是否有所需的行引用的 table 中存在主键值。一些消息来源声称触发器用于确保外键约束。
在 Postgres 中,master table 中的引用列需要具有 UNIQUE
或 PRIMARY KEY
约束。 Per documentation:
The referenced columns must be the columns of a non-deferrable unique or primary key constraint in the referenced table.
两者目前都总是 使用 btree 索引实现。所以在引用的列上总是一个btree索引:
- How does PostgreSQL enforce the UNIQUE constraint / what type of index does it use?
引用列根本不需要索引。如果 master table 中的行永远不会更新或删除,这可能就足够了。否则,引用列也应该被索引,但这不是由系统强制执行的。这只是关于性能优化,而不是数据完整性。
FK 约束本身的实际实现是系统目录中的一个条目pg_constraint
, a special internal trigger and another entry in pg_depend
。
我检查过,确实,PostgreSQL 没有为外键创建索引(使用此查询:)。
另一方面,为外键创建了一些触发器:
test=# SELECT tgname AS trigger_name
FROM pg_trigger
WHERE tgname !~ '^pg_';
trigger_name
--------------
(0 rows)
test=# ALTER TABLE LINEITEM ADD CONSTRAINT LINEITEM_FK1 FOREIGN KEY (L_ORDERKEY) REFERENCES ORDERS;
ALTER TABLE
test=# SELECT tgname AS trigger_name
FROM pg_trigger
WHERE tgname !~ '^pg_';
trigger_name
------------------------------
RI_ConstraintTrigger_a_16419
RI_ConstraintTrigger_a_16420
RI_ConstraintTrigger_c_16421
RI_ConstraintTrigger_c_16422
因此,我假设在 PostgreSQL 中创建外键期间,为引用的 table 创建了一个哈希映射,然后对引用的每一行执行探测 table。
有趣的是,MonetDB 为主键和外键创建了不同类型的索引(可能分别是 join-index 和 hash-index)。
sql>select * from sys.idxs;
+------+----------+------+-------------+
| id | table_id | type | name |
+======+==========+======+=============+
| 6467 | 6446 | 0 | orders_pk |
| 6470 | 6464 | 1 | lineitem_fk |
+------+----------+------+-------------+
2 tuples (3.921ms)
此外,Oracle 使用索引强制执行主键约束,默认情况下它不会为外键创建任何索引,但是,有一些外键索引技巧:https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:292016138754