【问题标题】:Database internals: implementation of a foreign key constraint数据库内部:外键约束的实现
【发布时间】:2015-05-17 21:04:23
【问题描述】:

外键是如何实现的,例如在 PostgreSQL 中?

我注意到在创建外键时涉及到很多散列,所以我假设在引用主键列的外键列上创建了一个基于散列的索引。如果是这样(例如,当我们想从被引用的表中删除一行时——这个带有主键的行或所谓的主表),我们可以很容易地检查被引用表中的行是否被实际引用。更重要的是,可能DBMS要求引用的主键列上至少有一个B+树索引,因为当我们要向引用表插入新行时,我们可以很容易地检查是否存在具有所需主键值的行在参考表中。一些消息来源声称使用触发器来确保外键约束。

【问题讨论】:

  • A(外键)约束是一个抽象概念。实现可以按照他们想要的任何方式实现它。在实践中,出于性能原因,几乎需要添加索引。出于理论上的原因,要求 FK 中的引用字段至少 是寻址目标表中的行的唯一方式。这在实践中通常意味着一个索引。
  • 是的,我知道主(或唯一)键上有一个 b+tree 索引,但是引用列上是否有任何其他结构(例如,基于哈希的索引)( s)?

标签: database postgresql database-design foreign-keys


【解决方案1】:

在 Postgres 中,主表中的引用列需要有 UNIQUE 或 PRIMARY KEY 约束。 Per documentation:

引用的列必须是不可延迟的唯一列 或引用表中的主键约束。

这两者目前总是使用 btree 索引实现。所以在被引用的列上总是有一个 btree 索引:

引用列根本不需要被索引。如果主表中的行从不更新或删除,这可能就足够了。否则,引用列也应该被索引,但这不是由系统强制执行的。这只是关于性能优化,而不是数据完整性。

FK约束本身的实际实现是系统目录pg_constraint中的一个条目,一个特殊的内部触发器和pg_depend中的另一个条目。

【讨论】:

  • 感谢您的回答。据我所知,b+tree 索引是在定义新主键时默认创建的(因此这是一项 CPU 密集型任务,因为在批量模式下创建 b+tree 之前必须对行进行排序)。我观察到在 postgres 中创建 FK 涉及很多哈希,所以我推断除了新条目之外,可能在引用列上创建了一个额外的结构(可能是基于哈希的索引) pg_constraint 和 pg_depend.
  • @adam.cajf:除了我已经列出的结构之外,没有创建其他结构。你观察到的是关系完整性的验证。创建后,Postgres 必须确定现有值服从新约束。
  • 是的,检查值是否存在是有意义的,但在这种情况下,应该使用主键上的 b+tree。那么,为什么会有很多散列呢?
  • @adam.cajf:不知道你的意思。但无论如何,在访问大部分表格时,索引几乎没有用处。该索引可能不会用于此特定目的。
  • 您可能看到的是在主键和外键之间执行外连接以检测不在主键中的外键条目。
【解决方案2】:

我检查了,确实,PostgreSQL 没有为外键创建索引(使用此查询:https://stackoverflow.com/a/25596855/1245175)。

另一方面,为外键创建了一些触发器:

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 中创建外键的过程中,会为被引用的表创建一个哈希映射,然后对引用表的每一行执行一次探测。

有趣的是,MonetDB 为主键和外键创建了不同类型的索引(可能分别是连接索引和哈希索引)。

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

【讨论】:

    猜你喜欢
    • 2011-04-09
    • 1970-01-01
    • 2021-11-03
    • 1970-01-01
    • 2018-09-17
    • 2013-12-17
    • 2018-01-23
    • 1970-01-01
    • 2011-04-30
    相关资源
    最近更新 更多