PostgreSQL fillfactor参数详解

1 基本概念

PostgreSQL每个表和索引的数据都是由很多个固定尺寸的页面存储(通常是 8kB,不过在编译服务器时[–with-blocksize]可以选择其他不同的尺寸)

PostgreSQL中数据操作永远是Append操作,具体含义如下:

  insert 时向页中添加一条数据
  update 将历史数据标记为无效,然后向页中添加新数据
  delete 将历史数据标记为无效  

因为这个特性,所以需要定期对数据库vacuum,否则会导致数据库膨胀,建议打开autovacuum.

ctid

(0,59)表示数据存放位置为第0个页面的第59行

更多页信息请参看PostgreSQL文档.

2 验证

/****************************************************************************************
    创建测试表
    test1设置fillfactor=100
    test2设置fillfactor=80
    drop table if exists  test1;
    drop table if exists  test2;
****************************************************************************************/
create table test1(
    objectid bigserial not null,                --唯一编号,主键
    name text not null,                         --名称
    describe text,                              --备注
    generate timestamptz default now() not null,--创建日期
    constraint pk_test1_objectid primary key(objectid)
)with (fillfactor=100);
alter table test1 cluster on pk_test1_objectid;

create table test2(
    objectid bigserial not null,                --唯一编号,主键
    name text not null,                         --名称
    describe text,                              --备注
    generate timestamptz default now() not null,--创建日期
    constraint pk_test2_objectid primary key(objectid)
)with (fillfactor=80);
alter table test2 cluster on pk_test2_objectid;

/****************************************************************************************
    创建随机生成中文字符函数
drop function if exists gen_random_zh(int,int);
****************************************************************************************/
create or replace function gen_random_zh(int,int)
    returns text
as $$
	select string_agg(chr((random()*(20901-19968)+19968 )::integer) , '')  from generate_series(1,(random()*($2-$1)+$1)::integer);
$$ language sql;


/****************************************************************************************
    导入测试数据
****************************************************************************************/
insert into test1(name)
  select gen_random_zh(8,32) from generate_series(1,10000);
insert into test2(name)
    select gen_random_zh(8,32) from generate_series(1,10000);
/****************************************************************************************
    为保证执行计划准确,数据导入完成后执行vacuum
****************************************************************************************/
vacuum freeze VERBOSE analyze test1;
vacuum freeze VERBOSE analyze test2;

根据ctid来查看数据分布情况

/****************************************************************************************
    查看test1数据在页中的布局
****************************************************************************************/
select ctid,objectid from test1 limit 500;
 ...
(0,50) |       50
(0,51) |       51
(0,52) |       52
(0,53) |       53
(0,54) |       54
(0,55) |       55
(0,56) |       56
(0,57) |       57
(0,58) |       58
(0,59) |       59
(0,60) |       60
(0,61) |       61
(0,62) |       62
(0,63) |       63
(0,64) |       64
(0,65) |       65
(0,66) |       66
(0,67) |       67
(0,68) |       68
(0,69) |       69
(0,70) |       70
(0,71) |       71
(0,72) |       72
(0,73) |       73
(0,74) |       74
(0,75) |       75
(1,1)  |       76
(1,2)  |       77
(1,3)  |       78
(1,4)  |       79
(1,5)  |       80
(1,6)  |       81
(1,7)  |       82
(1,8)  |       83
(1,9)  |       84
(1,10) |       85
(1,11) |       86
(1,12) |       87
(1,13) |       88
(1,14) |       89
(1,15) |       90
(1,16) |       91
(1,17) |       92
(1,18) |       93
 ...

在test1中,使用的填充率为100%,可以看到每页大约可以存储75条数据(存储的数据在75左右上下浮动)

/****************************************************************************************
    查看test2数据在页中的布局
****************************************************************************************/
select ctid,objectid from test2 limit 500;
...
(0,50) |       50
(0,51) |       51
(0,52) |       52
(0,53) |       53
(0,54) |       54
(0,55) |       55
(0,56) |       56
(0,57) |       57
(0,58) |       58
(0,59) |       59
(0,60) |       60
(0,61) |       61
(1,1)  |       62
(1,2)  |       63
(1,3)  |       64
(1,4)  |       65
(1,5)  |       66
(1,6)  |       67
(1,7)  |       68
(1,8)  |       69
(1,9)  |       70
(1,10) |       71
(1,11) |       72
(1,12) |       73
(1,13) |       74
(1,14) |       75
(1,15) |       76
(1,16) |       77
(1,17) |       78
(1,18) |       79
(1,19) |       80
(1,20) |       81
(1,21) |       82
(1,22) |       83
(1,23) |       84
(1,24) |       85
(1,25) |       86
(1,26) |       87
(1,27) |       88
(1,28) |       89
(1,29) |       90
(1,30) |       91
(1,31) |       92
(1,32) |       93
 ...

在test2中,使用的填充率为80%,可以看到每页大约可以存储59条数据,和test1比较每页的存储率大概率为61/75=81%,大概符合80%的填充率.

3 表膨胀测试

现在分别修改test1和test2中修改objectid为93的记录.

--test1
select ctid from test1 where objectid = 93;
ctid  
--------
(1,18)
(1 row)
update test1 set name=gen_random_zh(8,32) where objectid = 93;  
select ctid from test1 where objectid = 93;
ctid   
----------
(133,31)
(1 row)

--test2
select ctid from test2 where objectid = 93;
ctid  
--------
(1,32)
(1 row)
update test2 set name=gen_random_zh(8,32) where objectid = 93;
select ctid from test2 where objectid = 93;
ctid  
--------
(1,58)
(1 row)

可以看到test1中因为填充率为100%,update后第一页中没有位置存储新的数据了,所以检查最大的页文件是否还有位置,如果有直接插入,如果没有则再新建一页后插入,在本例中跳过了132个页文件.

test2中因为填充率为80%,还有20%的空间可以存储数据,因此update后直接在历史数据所在的页后面插入数据.

结论

  • autovacuum非常重要,必须要打开并设置合适的参数
  • fillfactor会降低insert的性能,但是update和delete性能将有提升
  • 根据需求选择合适的fillfactor,建议在建表时指定,不要使用默认值

猜你喜欢

转载自blog.csdn.net/kmblack1/article/details/80860390