PostgreSQL每个表和索引的数据都是由很多个固定尺寸的页面存储(通常是 8kB,不过在编译服务器时[–with-blocksize]可以选择其他不同的尺寸)
PostgreSQL中数据操作永远是Append操作,具体含义如下:
insert 时向页中添加一条数据
update 将历史数据标记为无效,然后向页中添加新数据
delete 将历史数据标记为无效
因为这个特性,所以需要定期对数据库vacuum,否则会导致数据库膨胀,建议打开autovacuum.
ctid
(0,59)表示数据存放位置为第0个页面的第59行
更多页信息请参看PostgreSQL文档.
/****************************************************************************************
创建测试表
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%的填充率.
现在分别修改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后直接在历史数据所在的页后面插入数据.