未使用 smallint[] 列上的 GIN 索引或错误“运算符不是唯一的”

OTA*_*TAR 2 postgresql indexing performance operator-overloading postgresql-9.5

create table test(
    id serial primary key,
    tagged smallint[]
);
Run Code Online (Sandbox Code Playgroud)

tagged列上有 gin 索引,带有_int2_ops操作符类:

CREATE INDEX ix ON test USING GIN(col _int2_ops);

当我运行此查询时:

select * from test
where tagged @> ARRAY[11]
order by id limit 100;
Run Code Online (Sandbox Code Playgroud)

EXPLAIN ANALYZE 显示:

限制(cost=0.43..19524.39 rows=100 width=36)(实际时间=25024.124..25027.263 rows=100 loops=1)
  -> 使用 test_pkey 进行索引扫描(cost=0.43..508404.37 rows=2604 width=36)(实际时间=25024.121..25027.251 rows=100 loops=1)
        过滤器:((标记)::integer[] @> '{11}'::integer[])
        过滤器删除的行数:2399999
规划时间:6.912 毫秒
执行时间:25027.307 毫秒

大胆强调我的。为什么将tagged列转换为integer[]类型?我认为这就是为什么不使用 GIN 索引并且查询运行缓慢的原因。

我试过了,WHERE tagged @> ARRAY[11]::smallint[]但得到了这个错误:

operator is not unique: smallint[] @> smallint[]
Run Code Online (Sandbox Code Playgroud)

如果我做同样的事情但使用tagged int[]和创建索引

CREATE INDEX ix ON test USING GIN(tagged gin__int_ops);
Run Code Online (Sandbox Code Playgroud)

那么上面的查询使用 GIN 索引:

"->  Bitmap Index Scan on ix  (cost=0.00..1575.53 rows=2604 width=0) (actual time=382.840..382.840 rows=2604480 loops=1)"
"   Index Cond: (tagged @> '{11}'::integer[])"
Run Code Online (Sandbox Code Playgroud)

这比以前快一点,但平均需要 10 秒 - 仍然太慢。我想尝试smallint[]而不是int[],也许那会更快......

Erw*_*ter 5

解决方案

最有可能的解决方案是对操作符进行模式限定:

SELECT *
FROM   test
WHERE  tagged OPERATOR(pg_catalog.@>) '{11}'::int2[]
ORDER  BY id
LIMIT  100;
Run Code Online (Sandbox Code Playgroud)

为什么?

这是运算符解析的问题(结合类型解析和强制转换上下文)。

在标准 Postgres 中,只有一个候选 operator anyarray @> anyarray,这就是您想要的。

如果您没有安装额外的模块intarray(我的假设),您的设置就可以正常工作,它为integer[] @> integer[].

因此,另一种解决方案是integer[]改用并具有gin__int_ops操作符类的 GIN 索引。或者尝试(intarray 的默认值)gist__int_ops索引。两者都可能更快,但都不允许 NULL 值。
或者您可以重命名intarray运算符@>以消除歧义。(我不会那样做。随之而来的是升级和便携性问题。)

对于涉及至少一个 type 操作数的表达式integer[],Postgres 知道选择哪个操作符:intarray 操作符。但是该索引不适用,因为 intarray 运算符只对integer( int4) not 进行操作int2。并且索引严格绑定到运算符:

但是对于int2[] @> int2[],Postgres 无法确定最佳运算符。两者似乎同样适用。由于pg_catalog模式中提供了默认运算符,而模式中提供了 intarray 运算符public(默认情况下 - 或安装扩展的任何地方),您可以通过使用OPERATOR()构造对运算符进行模式限定来帮助解决难题。有关的:

您收到的错误消息有点误导。但是,如果您仔细观察,会HINT添加一行提示(tada!)指向正确的方向:

ERROR:  operator is not unique: smallint[] @> smallint[]
LINE 1: SELECT NULL::int2[] @> NULL::int2[]
                            ^
HINT:  Could not choose a best candidate operator. You might need to add explicit type casts.
Run Code Online (Sandbox Code Playgroud)

您可以调查现有的运营商候选人@>

SELECT o.oid, *, oprleft::regtype, oprright::regtype, n.nspname
FROM   pg_operator o
JOIN   pg_namespace n ON n.oid = o.oprnamespace
WHERE  oprname = '@>';
Run Code Online (Sandbox Code Playgroud)

另一种替代解决方案是临时(!)设置不同的 search_path,因此只能找到所需的运算符。在同一个交易中:

SET LOCAL search_path = pg_catalog;
SELECT ...
Run Code Online (Sandbox Code Playgroud)

但是,您必须对查询中的所有表进行模式限定。

关于演员背景:

可以更改castcontextof int2-> int4。但我强烈建议不要这样做。太多可能的副作用: