pg中删除表中记录后,通过vacuum命令可以回收那些dead tulpe占用的空间。那么vacuum具体是怎么回收空间的呢?空间回收之后会影响原先表上索引的正常使用吗?
要弄清楚这些问题,我们那首先要知道在pg中heap page内部的空间分配是什么样的。lp内容即为对应tuple在当前页的OFFSET,lp的位置固定长度固定(固定page head后面就是lp),lp固定方便tuple的搜索(例如ctid的第二个部分就是lp信息)。
lp从page head开始分配,tuple从page tail开始分配,lp指向对应tuple的开始位置。
例子:
1、创建测试表
bill@bill=>create table c (id int, info text);
CREATE TABLE
2、安装pageinspect插件
bill@bill=>create extension pageinspect ;
CREATE EXTENSION
3、插入数据
bill@bill=>insert into c select id, repeat(md5(random()::text), 16) from generate_series(1,20) t(id);
INSERT 0 20
4、查看变长字段大小
每列变长字段大小516字节
bill@bill=>select pg_column_size(repeat(md5(random()::text), 16));
pg_column_size
----------------
516
(1 row)
5、查看第一页
lp已分配到80字节处
lp offset已分配到576字节处。
剩余只有不到496字节的空间。
bill@bill=>SELECT * FROM page_header(get_raw_page('c', 0));
lsn | checksum | flags | lower | upper | special | pagesize | version | prune_xid
-------------+----------+-------+-------+-------+---------+----------+---------+-----------
19/1621C910 | 0 | 0 | 80 | 576 | 8192 | 8192 | 4 | 0
(1 row)
6、查看每一条记录tuple在page内部的offset
bill@bill=>select lp,lp_off from heap_page_items(get_raw_page('c', 0));
lp | lp_off
----+--------
1 | 7648
2 | 7104
3 | 6560
4 | 6016
5 | 5472
6 | 4928
7 | 4384
8 | 3840
9 | 3296
10 | 2752
11 | 2208
12 | 1664
13 | 1120
14 | 576
(14 rows)
7、删除13条记录
bill@bill=>delete from c where ctid not in ('(0,1)','(0,3)','(0,5)','(0,7)','(0,9)','(0,11)','(0,13)');
DELETE 13
8、查看lp信息和lp offset信息,现在还没有垃圾回收掉。
bill@bill=>select lp,lp_off from heap_page_items(get_raw_page('c', 0));
lp | lp_off
----+--------
1 | 7648
2 | 7104
3 | 6560
4 | 6016
5 | 5472
6 | 4928
7 | 4384
8 | 3840
9 | 3296
10 | 2752
11 | 2208
12 | 1664
13 | 1120
14 | 576
(14 rows)
9、vacuum回收
bill@bill=>vacuum VERBOSE c;
INFO: vacuuming "bill.c"
INFO: "c": removed 13 row versions in 2 pages
INFO: "c": found 13 removable, 7 nonremovable row versions in 2 out of 2 pages
DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 12076871
There were 0 unused item identifiers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
INFO: "c": truncated 2 to 1 pages
DETAIL: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
INFO: vacuuming "pg_toast.pg_toast_226382"
INFO: index "pg_toast_226382_index" now contains 0 row versions in 1 pages
DETAIL: 0 index row versions were removed.
0 index pages have been deleted, 0 are currently reusable.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
INFO: "pg_toast_226382": found 0 removable, 0 nonremovable row versions in 0 out of 0 pages
DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 12076872
There were 0 unused item identifiers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
VACUUM
10、再次查看lp信息和lp offset信息
垃圾回收后,lp信息都还在,但是,请注意lp里面存储的offset发生了变化,被回收的tuple,发生了MOVE,变紧凑了,并没有出现空洞。
被删除的tuple对应的lp, offset全部变成了0。
bill@bill=>select lp,lp_off from heap_page_items(get_raw_page('c', 0));
lp | lp_off
----+--------
1 | 7648
2 | 0
3 | 7104
4 | 0
5 | 6560
6 | 0
7 | 6016
8 | 0
9 | 5472
10 | 0
11 | 4928
12 | 0
13 | 4384
14 | 0
(14 rows)
11、插入一条新记录后查看
2号lp被用起来了,OFFSET到了2312字节。
扫描二维码关注公众号,回复:
11017906 查看本文章
bill@bill=>insert into c select 100, repeat(md5(random()::text), 60);
INSERT 0 1
bill@bill=>select lp,lp_off from heap_page_items(get_raw_page('c', 0));
lp | lp_off
----+--------
1 | 7648
2 | 2432
3 | 7104
4 | 0
5 | 6560
6 | 0
7 | 6016
8 | 0
9 | 5472
10 | 0
11 | 4928
12 | 0
13 | 4384
14 | 0
(14 rows)
总结:
1、垃圾回收时,lp本身不变,这样索引可以保持不变。
2、页内move row , 只是lp offset 变化, 好处是PAGE内部不会膨胀,提高空间利用率。