天天看点

PostgreSQL GIN索引limit慢的原因分析

postgresql gin索引的结构如下图 :

假设这个表有2列,一列存储int,另一列存储int数组,最左边的表示记录的行号。

PostgreSQL GIN索引limit慢的原因分析

假设对int数组建立gin索引,那么gin索引会记录每个数组element对应的行号,对于行号多的,会存成list,然后在索引中指向该list。

PostgreSQL GIN索引limit慢的原因分析

好了接下来分析一下limit慢的原因, 实际上和gin索引的扫描方法有关,目前gin 索引只支持bitmap index scan,也就是说,会将所有匹配的行号取出,排序,然后去heap表取记录。

那么不管你limit多小,根据行号排序是免不了的,这就是limit比btree索引以及gist索引等不需要bitmap index scan的其他索引方法慢的原因。

例子:

数组匹配,走索引,注意是bitmap index scan,所以被匹配的数组对应有1万条记录的话,这1万条记录的行号会先排序,然后扫描heap取出记录。

因为走bitmap index scan, 所以即使加了limit 1,行号排序少不了,开销是不小的。

上面就是gin 索引limit慢的原因。

但是gin这么设计是有原因的,因为数组中可能存在大量的重复值。

例如我需要找的element有3个1,2,3,假设一共有10万条记录.

而1,2,3对应的ctid中可能存在大量重复的page,那么使用bitmap index scan就可以大大减少离散扫描的情况。

对于获取大量离散存放的堆数据是有奇效的。

而如果获取的记录数比较少,并且数据库的shared buffer足够大的话,完全没有必要bitmap index scan效果一般。

下面扩展一下,另一个例子,使用btree_gin使得一些标准类型也支持gin索引,因此可以用它来建立联合索引。

联合索引一般用在一个字段选择性不好,但几个字段组合起来选择性就比较好的情况。

例子

gin的联合索引用在什么地方比较好?

使用索引对应字段上的条件可以将范围缩小到很小的场景。

如果不能这样,或者是btree就可以缩小到很小的范围,那么建议使用btree就够了。

或者是说使用了limit限制要取的记录数,那么使用btree也是更好的,因为btree可以走index scan也可以走bitmap index scan。适用于小数据量查询,也适用于大数据量查询。