【发布时间】:2022-01-01 03:17:52
【问题描述】:
Postgres 13.4.
我有一个设置,其中包含 uuid 在内的混合字段类型的 Bloom 索引可能非常有用。开箱即用,Bloom 扩展仅支持int4 和text。如何添加对其他类型的支持?
【问题讨论】:
-
这样好多了!
标签: postgresql indexing operators bloom-filter
Postgres 13.4.
我有一个设置,其中包含 uuid 在内的混合字段类型的 Bloom 索引可能非常有用。开箱即用,Bloom 扩展仅支持int4 和text。如何添加对其他类型的支持?
【问题讨论】:
标签: postgresql indexing operators bloom-filter
根据 Laurenz Albe 的 cmets,我已将帖子改写为问答格式。
每隔几年,我都会从 Postgres Professional 的人员那里完成关于索引的精彩的十部分系列。我正在查看有关 Bloom 过滤器 (https://habr.com/en/company/postgrespro/blog/452968/) 的最后一篇文章,并发现了一个如何扩展对其他数据类型的支持的示例。它引起了我的注意,因为我以前没有弄清楚CREATE OPERATOR CLASS。我也没有找到在 Postgres 的 Bloom 索引的其他讨论中提到的这些细节。为了存档,我想我会写一些笔记,并希望能吸引一些额外的信息和/或更正。
Postgres 中的索引选项绝对是其十大功能之一。各种基本索引类型/框架和变体令人难以置信。不幸的是,pg_catalog 的索引相关部分非常密集。如果您有想要索引的内容,并且不支持该数据类型,那么您似乎可以使用CREATE OPERATOR CLASS 添加对其他类型的支持。在看到https://habr.com/en/company/postgrespro/blog/452968/ 的示例之前,我从来没有想过如何做到这一点。虽然这个示例和下面的代码专门针对 Bloom 过滤器索引,但我希望大部分机制可以转移到其他索引类型。
我今天不需要 Bloom 过滤器,并且可能永远想要一个。但是,老实说,它们非常酷*。在 Postgres 中了解一个很棒的功能,因为它们解决了一个您通常不会期望 RDBMS 有解决方案的问题。 (宽列,跨许多领域的随机搜索组合。)
我以前没有在 RDMBS 系统中看到布隆过滤器。我在分布式日志聚合系统中看到过它们,它们有助于减少来自远程位置或冷存储的不必要且昂贵的数据负载。 Postgres Bloom 过滤器与此不同。
我可能会很好地使用 Bloom 索引,因为我们有一个包含 13 个 UUID 列的宽表,这些列以任意数量的组合进行搜索,以及各种 int4 和其他数据。我喜欢提前试验这些东西,这样,如果出现问题,我已经有了可能的工具。而且,布隆过滤器不是 DBA 通常会立即想到的。但是,Bloom 索引是CREATE OPERATOR CLASS 的一个很好的示例。启动并运行它只需要三个步骤:
安装bloom 扩展(如果尚未安装)。
为感兴趣的数据类型和bloom 访问方法定义一个新的运算符类。这需要找到一个合适的散列函数。
使用新的运算符类创建新索引。
假设您已经安装了 bloom 扩展 (https://www.postgresql.org/docs/current/bloom.html),这里有一个小搜索来显示与 bloom 索引关联的运算符类:
select pg_am.amname,
pg_opclass.opcintype::regtype,
pg_opclass.opcname,
pg_am.amhandler::regproc
from pg_opclass
left join pg_am on pg_am.oid = pg_opclass.opcmethod
where amname = 'bloom'
order by amname,opcname;
+--------+-----------+---------------+-----------+
| amname | opcintype | opcname | amhandler |
+--------+-----------+---------------+-----------+
| bloom | integer | int4_ops | blhandler |
| bloom | text | text_ops | blhandler |
+--------+-----------+---------------+-----------+
所以,uuid 数据没有帮助。
这是这个简单案例所需的代码:
CREATE OPERATOR CLASS uuid_ops
DEFAULT FOR TYPE uuid USING bloom AS
OPERATOR 1 =(uuid,uuid),
FUNCTION 1 uuid_hash;
我认为上面的代码或多或少意味着“OPERATOR strategy 1 EQUALITY hash”。我猜想将运算符类添加到具有更广泛运算符的索引类型会更复杂。布隆过滤器仅支持“可能存在/不存在”逻辑,因此只需要一个声明,以及要匹配的哈希函数。如果你再次运行前一个,它现在返回一个新操作符类的条目:
+--------+-----------+---------------+-----------+
| amname | opcintype | opcname | amhandler |
+--------+-----------+---------------+-----------+
| bloom | integer | int4_ops | blhandler |
| bloom | text | text_ops | blhandler |
| bloom | uuid | uuid_ops | blhandler |
+--------+-----------+---------------+-----------+
不久前有人告诉我,虽然没有在文档中列出,但 Postgres 源代码包含大量特定于类型的哈希函数。为哈希索引实现了一整套类型特定的函数:
https://doxygen.postgresql.org/hashfunc_8c_source.html
我在那里没有看到uuid,但是这里实现了一个哈希:
https://github.com/postgres/postgres/blob/REL_13_STABLE/src/backend/utils/adt/uuid.c#L403
您只需要知道提供CREATE OPERATOR CLASS 的函数的名称,而不是它在源代码中的实现位置。
有了这个,就可以在 uuid 数据上创建一个 Bloom 索引。这是一个好主意吗?不知道。我今天需要 Bloom 指数吗?不。但是我喜欢通过 Postgres 的功能来了解当我确实遇到合适的问题时可以使用什么。 Postgres 中的索引比我知道或见过的任何其他东西都要广泛得多……它们需要一些研究。下面是一个在 Bloom 索引上使用新运算符类的示例,该索引包括十个 uuid 字段,后跟三个 int4 字段。
create index analytic_work_bloom_idx on data.analytic_work
using bloom(
id,
hsys_id,
facility_id,
inv_id,
user_id,
activity_id,
assembly_id,
q_event_id,
scan_id,
scase_id,
expected_count,
actual_count,
missing_count)
with (length=96,
col1 = 7,
col2 = 7,
col3 = 7,
col4 = 7,
col5 = 7,
col6 = 7,
col7 = 7,
col8 = 7,
col9 = 7,
col10 = 7,
col11 = 7,
col12 = 7,
col13 = 7);
select pg_size_pretty(pg_total_relation_size('analytic_work_bloom_idx')); --- Curious.
不要将上面的示例作为如何计算 Bloom 索引的正确签名长度和每列字节数的一个很好的示例。有很多文章解释了调整签名长度和每列字节数的机制、理论和建议方法。超过了我的工资等级。
我在示例数据库中有大约 600K+ 行,并添加了 Bloom 索引。结果应该是相当多变的,但就我而言,我发现:
bloom 索引大约是一个 uuid 字段上的 B 树的 4 倍。我很高兴看到人们提供的任何额外信息或更正。
【讨论】: