brin索引是postgresql9.5版本中新增的功能,这个索引的特点就是占用空间特别小,原理是这样的,它是将表的数据页面按每128个数据块(页面)分配一条索引记录,记录这个区间的最大值和最小值,当你要查询某条数据时,判断这个值在哪个区间范围内,找到呢条索引记录,根据索引记录扫描相应的128个数据块。128这个值是默认的,在创建索引的时候可以进行调整的。下面用pageinspect这个插件对brin索引进行下简单的分析。
创建测试表,安装pageinspect,创建brin索引。
postgres=# create extension pageinspect;
CREATE EXTENSION
postgres=# create table a(id int);
CREATE TABLE
postgres=# insert into a select generate_series(1,3000000);
INSERT 0 3000000
postgres=# create index ix_a_id_brin on a using brin(id) with (pages_per_range=128);
CREATE INDEX
下面看下要用到的pageinspect的函数
postgres=# df public.brin*
List of functions
Schema | Name | Result data type | Argument data types
| Type
public | brin_metapage_info | record | page bytea, OUT magic text, OUT version integer, OUT pagesperrange integer, OUT lastrevmappage bigi |
---|---|---|---|
nt | normal | ||
public | brin_page_items | SETOF record | page bytea, index_oid regclass, OUT itemoffset integer, OUT blknum integer, OUT attnum integer, OUT |
allnulls boolean, OUT hasnulls boolean, OUT placeholder boolean, OUT value text | normal | ||
public | brin_page_type | text | page bytea |
normal | |||
public | brin_revmap_data | SETOF tid | page bytea, OUT pages tid |
(4 rows)
brin_page_type这个是查看索引页面类型的,主要有三种类型meta,revmap和regular,因为这个索引只有三个页面所以只看了0,1,2这三个页面。
postgres=# select * from brin_page_type(get_raw_page('ix_a_id_brin',0));
brin_page_type
meta
(1 row)
postgres=# select * from brin_page_type(get_raw_page('ix_a_id_brin',1));
brin_page_type
revmap
(1 row)
postgres=# select * from brin_page_type(get_raw_page('ix_a_id_brin',2));
brin_page_type
regular
(1 row)
postgres=# select relpages from pg_class where relname='ix_a_id_brin';
relpages
3
(1 row)
下面看下这三个页面都是存的什么数据。
meta页面
postgres=# select * from brin_metapage_info(get_raw_page('ix_a_id_brin',0));
magic | version | pagesperrange | lastrevmappage |
---|---|---|---|
0xA8109CFA | 1 | 128 | 1 |
这个是说这个brin索引记录128个块中值得范围大小,最后一个revmap页面是1号页面。和之前查看页面类型页面号对应。
revmap页面
postgres=# select * from brin_revmap_data(get_raw_page('ix_a_id_brin',1)) limit 10;
pages
(2,1)
(2,2)
(2,3)
(2,4)
(2,5)
(2,6)
(2,7)
(2,8)
(2,9)
(2,10)
(10 rows)
postgres=# select * from brin_revmap_data(get_raw_page('ix_a_id_brin',1)) offset 100 limit 10;
pages
(2,101)
(2,102)
(2,103)
(2,104)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(10 rows)
postgres=# select count(1) from brin_revmap_data(get_raw_page('ix_a_id_brin',1)) ;
count
1360
(1 row)
postgres=# select relpages from pg_class where relname='a';
relpages
13275
(1 row)
revmap页面能存储1360条记录,可以看到最后一条记录是104,这个104是怎么算的呢?
首先查出a表一共的页面数13275,brin索引的每条记录对应128个块,13275/128=103.7,所以是104。
这个还可以看出索引条目在regluar页面的分布情况,因为我测试的记录条数较少,所以都在2号regluar页面上。
regluar页面
postgres=# select * from brin_page_items(get_raw_page('ix_a_id_brin',2),'ix_a_id_brin') limit 10;
(10 rows)
postgres=# select * from brin_page_items(get_raw_page('ix_a_id_brin',2),'ix_a_id_brin') offset 94 limit 10;
(10 rows)
blknum是该索引记录对应的起始页面编号,第一条就是0,第二条就是128。attnum是指存放第几个字段的意思,我只有一个字段所以都是1,allnulls表示是不是都是空值,hasnulls表示是否存在空值。value就是记录索引对应的128个数据块中数据的最大值和最小值。