PgSQL内核特性 | Brin索引

PgSQL内核特性 | Brin索引

数据库在进行过滤扫描或者join时,如果该表特别大,那么就需要顺序扫描表的所有数据然后进行过滤,或者扫描所有数据进行join条件探测。这对IO的负载影响特别大,当在join时,比如HashJoin的外表,需要对每个数据都进行Hash表探测,进一步影响性能。

现有列存比如Infobright、Parquet、hydra等都对存储进行了类似的改进,在每个block中增加了最大值和最小值的统计信息,这样就方便进行过滤,提前将不满足条件的列存数据block过滤掉而不进行加载,能够减少IO提升性能。当然这对于一定规则,比如有序或者较为有序的存储效果尤为明显,否则如果过滤的block不多,效果甚至会恶化。

PgSQL中同样提供了类似的功能,也就是Brin索引(Block Range Index)。下面我们介绍下Brin索引的实现原理。

1、Brin索引的结构

Brin索引的数据页有三种类型:meta页、revmap页和regular页。其中meta页存储一些元数据信息,包括brin版本、跨步的大小以及最后的revmap页号;revmap页存储表数据页一个范围内的最大值最小值构成的元组处于regular页的位置,即ItemPointerData结构:页号+偏移;regular页存储表数据页一个范围内的最大值最小值构成的元组。

第0页时meta页,随后跟着的时revmap页,然后时regular页。留给读者思考:当revmap页满需要扩展的时候,需要后面的regrular页腾出空间来给他使用,如此才保证meta页-revmap页-regular页的顺序,真是这样吗?这样的话岂不是insert的代价很大?

PgSQL内核特性 | Brin索引_第1张图片

2、如何通过Brin索引进行扫描

PgSQL内核特性 | Brin索引_第2张图片

插入的时候,如上图所示,将range范围内的所有block总的最大值和最小值构成一个tuple,将其插入regular页,该tuple位于该regrular的页号+偏移构成一个ItemPointerData,将其插入revmap中。可以看到range个blocks对应regular的一条记录,对应revmap的一个rm_tids[]成员。

那么如何根据brin索引进行查询呢?

Brin索引一般会和BitmapHeapScan算子结合起来使用,索引扫描使用BitmapIndexScan算子,该算子进行索引扫描,根据过滤条件,扫描Brin索引,得到每个range范围内的最大值和最小值,判断条件的值是否在这个范围内,如果在则将该range范围内的所有页的页号加入bitmap,否则跳过(即过滤掉了)。然后BitmapHeapScan算子根据bitmap加载对应页,进行顺序扫描,再次进行下条件判断。如此就会跳过不满足条件的页进行加载,大大减少IO。

构建bitmap的流程如下:

PgSQL内核特性 | Brin索引_第3张图片

你可能感兴趣的:(搜索引擎,数据库)