postgresql - 具有大量列的唯一索引
问题描述
我有一个繁忙的 OLTP 表,它有 30 列和 5000 万行,我想避免其中出现重复。
我应该采取什么方法?
到目前为止,我想出了这些:
- 30 列上的直接唯一索引(听起来像一个巨大的慢速索引)
- 某种计算的 30 列总和的唯一索引
- 应用程序端生成的哈希放置在新的第 31 列中,具有 btree 唯一索引
对于后者,我觉得如果表模式发生变化,重新生成该哈希列将会有很多麻烦。
也许还有其他一些我没有想到的方法?
解决方案
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 上为这个问题提供了一个解决方案:
这非常接近您的第三个想法:
基本上,一个非常有效的服务器端生成的哈希放置在第 31 列,有UNIQUE
约束。
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
语句,这样您就不会重写表两次。
或者,使用UNIQUE
基于 的表达式索引public.f_tbl_bighash()
。一样的效果。好处:没有额外的表格列。缺点:在计算上有点贵。
推荐阅读
- python - 错误:需要 Microsoft Visual C++ 14.0 或更高版本
- python - 在 python modulenotfounderror 中:没有名为“json”的模块
- python - Python np.fromfile 给出错误:Errno 2 No such file or directory
- android - 移动视频转码的最佳参数。请指教
- python - 如果不在值范围内,Python Pandas 会替换值
- python - Python 写入 GRIB 文件
- amazon-web-services - 我无法通过 SSH 连接到使用 Terraform 创建的 EC2 实例
- c# - Url.Action 返回错误值.net core 3.1
- php - MYSQL TimeDiff 如果使用 PHP 为 0,则不减去
- php - 使用 add_rewrite_rule 进行简单的页面重定向