Postgres - 创建EXCLUDE约束非常慢

时间:2017-03-25 05:00:11

标签: sql database postgresql psql

我们有一个表foo,其架构如下所示

hi=# \d foo
                       Table "public.foo"
   Column   |           Type           |       Modifiers
------------+--------------------------+------------------------
 id         | uuid                     | not null
 bar_id     | uuid                     | not null
 hi         | character varying(128)   | not null
 yo         | character varying(4000)  |
 updated_at | timestamp with time zone | not null default now()
 created_at | timestamp with time zone | not null default now()
Indexes:
    "foo$pk" PRIMARY KEY, btree (id)
    "foo$uk" UNIQUE CONSTRAINT, btree (bar_id, hi, yo)
Foreign-key constraints:
    "foo$bar$fk" FOREIGN KEY (bar_id) REFERENCES bar(id)

我们内部有大约100M的记录,正如您所看到的,此表有一个UNIQUE约束,我们要做的是用EXCLUDE约束替换它出于商业原因。所以我们想要做出的改变如下所示

ALTER TABLE foo ADD CONSTRAINT "foo$one$uk"
EXCLUDE ( bar_id WITH =, hi WITH =, yo WITH =) WHERE (hi = 'Tom') DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE foo ADD CONSTRAINT "foo$two$uk"
EXCLUDE ( bar_id WITH =, hi WITH =) WHERE (hi = 'Lisa') DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE foo DROP CONSTRAINT IF EXISTS "foo$uk";

有证据表明,使用(m3.large + 300GB通用SSD)在AWS RDS实例上运行这3个语句大约需要12个小时才能完成。但是我们也注意到第一个语句几乎一直消耗,第二个语句快速(几分钟内),第三个语句立即返回。所以我想知道幕后发生了什么,为什么会这样呢?

1 个答案:

答案 0 :(得分:4)

每当您添加约束时,都应检查现有数据以确保不存在现有的约束违规。

排除约束被描述为see 5.3.6

  

确保使用指定的运算符在指定的列或表达式上比较任何两行。

因此,根据有多少行hi = 'Tom',您可能会执行超过1亿行的O(n 2 )操作。是的,这需要一段时间。

另请注意:

  

添加排除约束将自动创建约束声明中指定类型的索引。

这有一些开销,但不如比较每对现有行。

关于第二个约束,我不确定,但有两种可能性,它为什么约束运行得更快。

要么显着减少行WHERE hi = 'Lisa',或者引擎可以利用先前约束已被检查的事实来获取信息,以便更有效地检查新约束。

显然,第3次更改,删除约束,不需要检查任何内容。

旁注

您可以选择在创建约束检查时禁用约束检查。 (我不知道PostgreSQL是否支持它。)

  • 这允许您忽略现有的约束违规,但确保继续检查约束。
  • 这会产生很大的加速约束创建的副作用。
  • 当然这也意味着约束尚未“验证”。这意味着引擎无法“信任”约束的完整性,从而获得可能实现的任何性能优势。