为什么添加外键关系后删除操作会变慢?

Tha*_*Guy 4 postgresql

想象一下关于猫主人的下表。

drop table if exists  owners cascade;
create table owners(
    id bigint primary key generated always as identity ,
    name text not null
)
;

insert into owners(name)
select random()::text from generate_series(1,20000);
--insert 200,000 owners records
Run Code Online (Sandbox Code Playgroud)

当我删除一些所有者记录时,速度非常快:

delete  from owners
where id %10 = 0;
Run Code Online (Sandbox Code Playgroud)

85 毫秒内影响了 20000 行

现在我添加一个名为“cats”的表,它指的是所有者:

drop table if exists cats;
create table cats(
    id serial primary key ,
    name varchar(20000) not null,
    owner_id int not null references owners(id)
);

--insert 1bn cats records
insert into cats(name, owner_id)
select
       random()::text,
       owners.id
from generate_series(1,10), owners;
Run Code Online (Sandbox Code Playgroud)

让我们删除一些所有者,但首先我们必须删除这些所有者“拥有”的猫:

--delete the records in cats so we don't get a foreign key constraint violation
delete  from cats
where owner_id %10 = 1;


---now we do the same delete on owners as we did before
delete  from owners
where id %10 = 1;
Run Code Online (Sandbox Code Playgroud)

在 25 秒 828 毫秒内影响了 2000 行

为什么所有者的第二次删除比我们没有 cat 表时慢 5000 倍?

a_h*_*ame 7

这是检查cats在 DELETE 期间表是否仍然引用所有者。检查基本上是使用select * from cats where owner_id = ?您删除的每个所有者完成的。

您可以通过在外键列上创建索引来加快检查速度:

create index on cats (owner_id);
Run Code Online (Sandbox Code Playgroud)

  • 根据我的经验,对于关系数据库的新手(有时是现在应该更了解的人!),假设索引是自动创建的,以支持强制执行每个外键所需的所有检查,这是很常见的。也许某些 DB 会这样做,或者一些混乱来自于通过索引实现/强制执行的主键和唯一约束。但是大多数 DB 不会,因为它可能非常浪费:应用程序访问模式可能意味着不需要它,或者其他索引可能支持足够的性能检查(在任何一种情况下,自动索引都会浪费空间、内存和 CPU)。 (3认同)
  • 这里的关键问题是 postgres 要求目标列 owner.id 具有索引,但反之则不然。也就是说,postgres 有效地要求插入到 cat 中要快,但不要求对所有者进行删除。 (2认同)