如何在不在 postgres 中创建大量索引的情况下进行唯一检查

ram*_*sus 2 postgresql index-tuning many-to-many

我需要对 INSERT 和 UPDATE 操作实施唯一检查,但我更愿意避免在我的表上创建大量唯一索引(现在约为 12Gb)。现在我有唯一的部分索引并且它工作得很好,除了一件事 - 它需要大量的 SSD 空间。我不需要使用这个索引的 SELECT 操作,我只需要检查数据的唯一性。

我阅读了这个这个讨论,我明白唯一约束和唯一索引之间没有真正的区别,除了部分条件,它只能用于索引。

有没有办法做到这一点?

更新:

我的例子是带有历史选项的多对多关系表的模式。此选项由 2 个附加字段time_fromtime_to. 他们正在存储进入和离开关系的时间。为了数据一致性,我创建了 4 个额外的部分唯一索引(见下文)。

现在该表包含 1 162 010 000 行。以及每个索引的表的整体空间:

vkontakte_wall_post_likes_users - 57 GB
vkontakte_wall_post_like_users_post_id - 24 GB
vkontakte_wall_post_like_users_time_to_2col_uniq - 24 GB
vkontakte_wall_post_like_users_pkey - 24 GB
vkontakte_wall_post_like_users_user_id - 24 GB
vkontakte_wall_post_like_users_time_to_3col_uniq - 846 MB
vkontakte_wall_post_like_users_time_from_2col_uniq - 8192 bytes
vkontakte_wall_post_like_users_id_seq - 8192 bytes
Run Code Online (Sandbox Code Playgroud)

架构:

CREATE TABLE vkontakte_wall_post_likes_users
(
  id integer NOT NULL DEFAULT nextval('vkontakte_wall_post_like_users_id_seq'::regclass),
  post_id integer NOT NULL,
  user_id integer NOT NULL,
  time_from timestamp with time zone,
  time_to timestamp with time zone,
  CONSTRAINT vkontakte_wall_post_like_users_pkey PRIMARY KEY (id),
  CONSTRAINT post_id_refs_id_3979681bdf0b31a3 FOREIGN KEY (post_id)
      REFERENCES vkontakte_wall_post (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED,
  CONSTRAINT user_id_refs_id_73bdbaad8e08aee5 FOREIGN KEY (user_id)
      REFERENCES vkontakte_users_user (remote_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED
)
WITH (
  OIDS=FALSE
);
ALTER TABLE vkontakte_wall_post_likes_users
  OWNER TO manufacture;

CREATE INDEX vkontakte_wall_post_like_users_post_id
  ON vkontakte_wall_post_likes_users
  USING btree
  (post_id);

CREATE UNIQUE INDEX vkontakte_wall_post_like_users_time_from_2col_uniq
  ON vkontakte_wall_post_likes_users
  USING btree
  (post_id, user_id)
  WHERE time_from IS NULL;

CREATE UNIQUE INDEX vkontakte_wall_post_likes_users_time_from_3col_uniq
  ON vkontakte_wall_post_likes_users 
  USING btree 
  (post_id, user_id, time_from)
  WHERE time_from IS NOT NULL;

CREATE UNIQUE INDEX vkontakte_wall_post_like_users_time_to_2col_uniq
  ON vkontakte_wall_post_likes_users
  USING btree
  (post_id, user_id)
  WHERE time_to IS NULL;

CREATE UNIQUE INDEX vkontakte_wall_post_like_users_time_to_3col_uniq
  ON vkontakte_wall_post_likes_users
  USING btree
  (post_id, user_id, time_to)
  WHERE time_to IS NOT NULL;

CREATE INDEX vkontakte_wall_post_like_users_user_id
  ON vkontakte_wall_post_likes_users
  USING btree
  (user_id);
Run Code Online (Sandbox Code Playgroud)

Cra*_*ger 5

如果您想有效地确保数据的唯一性,则无法避免创建唯一索引。唯一索引对于 PostgreSQL 在并发插入、更新和删除时强制执行唯一性是必要的。

唯一索引支持唯一约束;创建唯一约束时,会自动为您创建唯一索引。

强制唯一性的唯一其他方法是LOCK TABLE mytable IN EXCLUSIVE MODE在对表进行任何可能影响您希望使其唯一的列的更改之前进行。这样您就不必创建唯一索引,但作为交换,您一次只能有一个事务更改表。仅当它检测到唯一列中的更改时,您才能从触发器执行此操作,但随后您会遭受会导致频繁死锁的锁升级。

真的,唯一索引是这里的方法。

您可以通过使用CREATE UNIQUE INDEX ... CONCURRENTLY然后使用ALTER TABLE ... ADD CONSTRAINT ...指定用于UNIQUE约束的已创建索引的版本来减轻所需锁的影响。