树
我的问题是BTree索引会很大,因为它会存储重复的值(它也是如此,因为它不能假设表是物理排序的)。如果BTree很大,我最终不得不同时读取索引和索引所指向的表的各个部分...
不一定—具有“覆盖”的btree索引将是最快的读取时间,如果这就是您想要的(即,如果您能负担得起额外的存储空间),那么这是您的最佳选择。
布林
我的理解是,在这里我可以有一个小的索引,但以读取无用的页面为代价。使用较小的值pages_per_range
表示索引较大(这是BRIN的问题,因为我需要读取整个索引),使用较大的值pages_per_range
表示我将读取很多无用的页面。
如果您负担不起覆盖btree索引的存储开销,则BRIN非常适合您,因为您已经建立了集群(这对于BRIN有用至关重要)。BRIN索引很小,因此,如果选择合适的值,则所有页面都可能在内存中pages_per_range
。
是否有一个神奇的公式可以找到考虑到这些折衷因素的pages_per_range的良好价值?
没有魔术公式,但开头pages_per_range
要比平均值所占的平均大小(以页为单位)小一些a
。您可能正在尝试最小化:(典型扫描的)((扫描的BRIN页的数量)+(扫描的堆页面的数量)。查找Heap Blocks: lossy=n
在执行计划pages_per_range=1
,并与其他值进行比较的pages_per_range
-即看看有多少不必要的堆块进行扫描。
杜松子酒
由于它们主要用于全文搜索,因此不确定此处是否相关,但我也听说它们擅长处理重复键。GIN
/ GiST
索引在这里有帮助吗?
GIN可能值得考虑,但GiST可能不值得考虑-但是,如果自然聚类确实很好,那么BRIN可能会是一个更好的选择。
这是一个与您的虚拟数据不同的索引类型之间的示例比较:
表和索引:
create table foo(a,b,c) as
select *, lpad('',20)
from (select chr(g) a from generate_series(97,122) g) a
cross join (select generate_series(1,100000) b) b
order by a;
create index foo_btree_covering on foo(a,b);
create index foo_btree on foo(a);
create index foo_gin on foo using gin(a);
create index foo_brin_2 on foo using brin(a) with (pages_per_range=2);
create index foo_brin_4 on foo using brin(a) with (pages_per_range=4);
vacuum analyze;
关系大小:
select relname "name", pg_size_pretty(siz) "size", siz/8192 pages, (select count(*) from foo)*8192/siz "rows/page"
from( select relname, pg_relation_size(C.oid) siz
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where nspname = current_schema ) z;
名称| 尺寸 页数| 行/页
:----------------- | :------ | ----:| --------:
foo | 149 MB | 19118 | 135
foo_btree_covering | 56 MB | 7132 | 364
foo_btree | 56 MB | 7132 | 364
foo_gin | 2928 kB | 366 | 7103
foo_brin_2 | 264 kB | 33 | 78787
foo_brin_4 | 136 kB | 17 | 152941
覆盖btree:
explain analyze select sum(b) from foo where a='a';
| 查询计划|
| :------------------------------------------------- -------------------------------------------------- ------------------------------------------- |
| 总计(成本= 3282.57..3282.58行= 1宽度= 8)(实际时间= 45.942..45.942行= 1循环= 1)|
| ->仅索引在foo上使用foo_btree_covering进行扫描(成本= 0.43..3017.80行= 105907宽度= 4)(实际时间= 0.038..27.286行= 100000循环= 1)|
| 索引条件:(a ='a':: text)|
| 堆访存:0 |
| 计划时间:0.099毫秒|
| 执行时间:45.968 ms |
普通btree:
drop index foo_btree_covering;
explain analyze select sum(b) from foo where a='a';
| 查询计划|
| :------------------------------------------------- -------------------------------------------------- ----------------------------- | |
| 总计(成本= 4064.57..4064.58行= 1宽度= 8)(实际时间= 54.242..54.242行= 1循环= 1)|
| ->在foo上使用foo_btree进行索引扫描(成本= 0.43..3799.80行= 105907宽度= 4)(实际时间= 0.037.0.33.04行= 100000循环= 1)|
| 索引条件:(a ='a':: text)|
| 计划时间:0.135毫秒|
| 执行时间:54.280毫秒|
BRIN pages_per_range = 4:
drop index foo_btree;
explain analyze select sum(b) from foo where a='a';
| 查询计划|
| :------------------------------------------------- -------------------------------------------------- ----------------------------- | |
| 总计(成本= 21595.38..21595.39行= 1宽度= 8)(实际时间= 52.455..52.455行= 1循环= 1)|
| ->在foo上进行位图堆扫描(成本= 888.78..21330.61行= 105907宽度= 4)(实际时间= 2.738..31.967行= 100000循环= 1)|
| 重新检查条件:(a ='a':: text)|
| 通过索引删除的行重新检查:96 |
| 堆块:有损= 736 |
| ->在foo_brin_4上进行位图索引扫描(成本= 0.00..862.30行= 105907宽度= 0)(实际时间= 2.720..2.720行= 7360循环= 1)|
| 索引条件:(a ='a':: text)|
| 计划时间:0.101毫秒|
| 执行时间:52.501毫秒|
BRIN pages_per_range = 2:
drop index foo_brin_4;
explain analyze select sum(b) from foo where a='a';
| 查询计划|
| :------------------------------------------------- -------------------------------------------------- ----------------------------- | |
| 总计(成本= 21659.38..21659.39行= 1宽度= 8)(实际时间= 53.971..53.971行= 1循环= 1)|
| ->在foo上进行位图堆扫描(成本= 952.78..21394.61行= 105907宽度= 4)(实际时间= 5.286..33.492行= 100000循环= 1)|
| 重新检查条件:(a ='a':: text)|
| 通过索引删除的行重新检查:96 |
| 堆块:有损= 736 |
| ->在foo_brin_2上进行位图索引扫描(成本= 0.00..926.30行= 105907宽度= 0)(实际时间= 5.275..5.275行= 7360循环= 1)|
| 索引条件:(a ='a':: text)|
| 计划时间:0.095毫秒|
| 执行时间:54.016毫秒|
杜松子酒:
drop index foo_brin_2;
explain analyze select sum(b) from foo where a='a';
| 查询计划|
| :------------------------------------------------- -------------------------------------------------- ------------------------------ |
| 总计(成本= 21687.38..21687.39行= 1宽度= 8)(实际时间= 55.331..55.331行= 1循环= 1)|
| ->在foo上进行位图堆扫描(成本= 980.78..21422.61行= 105907宽度= 4)(实际时间= 12.377..33.956行= 100000循环= 1)|
| 重新检查条件:(a ='a':: text)|
| 堆块:确切= 736 |
| ->在foo_gin上进行位图索引扫描(成本= 0.00..954.30行= 105907宽度= 0)(实际时间= 12.271..12.271行= 100000循环= 1)|
| 索引条件:(a ='a':: text)|
| 计划时间:0.118毫秒|
| 执行时间:55.366毫秒|
dbfiddle 在这里