我有一个繁忙的 OLTP 表,包含 30 列和 5000 万行,我想避免其中出现重复。
我应该采取什么方法?
到目前为止我想出了这些:
- 30 列上的直接唯一索引(听起来像是一个巨大的缓慢索引)
- 某种计算得出的 30 列总和的唯一索引
- 应用程序端生成的哈希值放置在带有 btree 唯一索引的新第 31 列中
对于后者,我觉得如果表架构发生变化,重新生成该散列列将会有很多麻烦。
也许还有一些我没有想到的其他方法?
最佳答案
Postgres 14
...刚刚推出了一个用于记录的内置哈希函数,它比我的自定义函数便宜得多。尤其是对于很多专栏来说!请参阅:
这使得表达式索引比生成的列加索引更具吸引力。所以只是:
CREATE UNIQUE INDEX tbl_row_uni ON tbl (hash_record_extended(tbl.*,0));
这通常也有效:
CREATE UNIQUE INDEX tbl_row_uni ON tbl (hash_record_extended(tbl,0));
但第一个变体更安全。在第二个变体中,如果存在同名列,tbl
将解析为该列。
Postgres 13(原始答案)
我最近在 dba.SE 上提供了针对该问题的解决方案:
这与你的第三个想法非常接近:
基本上,一个非常高效的服务器端生成的哈希被放置在具有 UNIQUE
约束的第 31 列。
CREATE OR REPLACE FUNCTION public.f_tbl_bighash(col1 text, col2 text, ... , col30 text)
RETURNS bigint
LANGUAGE sql IMMUTABLE PARALLEL SAFE AS
'SELECT hashtextextended(textin(record_out(($1,$2, ... ,$30))), 0)';
ALTER TABLE tbl
ADD COLUMN tbl_bighash bigint NOT NULL GENERATED ALWAYS AS (public.f_tbl_bighash(col1, col2, ... , col30)) STORED -- append column in last position
, ADD CONSTRAINT tbl_bighash_uni UNIQUE (tbl_bighash);
它的美妙之处:它可以高效地工作而无需改变任何其他东西。 (除非您在没有目标列表或类似内容的情况下使用 SELECT *
或 INSERT INTO
。)
它也适用于 NULL
值(将它们视为相等)。
如果任何列类型具有不可变的文本表示形式,请小心。 (如 timestamptz
。)该解决方案使用所有 text
列进行测试。
如果表架构发生更改,请先删除 UNIQUE
约束,重新创建函数并重新创建生成的列 - 最好使用单个 ALTER TABLE
> 语句,这样您就不会重写该表两次。
或者,使用基于 public.f_tbl_bighash()
的 UNIQUE
表达式索引。效果一样。优点:没有额外的表列。缺点:计算成本较高。
关于postgresql - 具有大量列的唯一索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69330676/