postgres数据库中VM、FSM原理详解
·
vm可见性映射
定义:VM 是 PG 中每个表附带的辅助文件,用于快速判断数据块中是否有对所有事务都可见的元组。
核心作用:
- 加速 VACUUM 操作:跳过全表扫描,只清理包含死元组的块
- 加速 Index-Only Scan:当 VM 标记某块为"全可见"时,无需回表检查可见性
存储结构:
- 每个数据块(8KB)在 VM 中对应 2个比特位
- 第1位:标记该块是否对所有事务可见(全可见块)
- 第2位:标记该块是否需要被冻结(freeze)
文件位置:$PGDATA/base/<database_oid>/<table_oid>_vm
FSM空闲空间映射
定义:FSM 是 PG 中每个表附带的辅助文件,用于记录每个数据块中的剩余空闲空间。
核心作用:
- 加速 INSERT/UPDATE 操作:快速定位有足够空间的块,避免遍历所有块
- 优化空间利用率:优先将新数据填入已有空闲空间
存储结构:
- 采用树形结构(FSM Tree)组织
- 叶子节点:记录每个块的剩余空间(0-8192字节)
- 上层节点:记录子节点中的最大空闲空间
- 支持快速查找可用块
文件位置:$PGDATA/base/<database_oid>/<table_oid>_fsm
如何往一张表里插入10万行数据,int text,使用一个函数或随机值,一条sql
VM、FSM怎么创建的
- VM 文件:只在需要时才创建(第一次 VACUUM 或第一次 Index-Only Scan 时)
- FSM 文件:只在需要时才创建(第一次 INSERT 或 UPDATE 需要查找空闲空间时)
执行 VACUUM(会触发 VM 和 FSM 更新)
VACUUM tablename;
VACUUM (VERBOSE, ANALYZE) tablename;
## VERBOSE=输出详细执行日志
实验
1.创建数据库mydb1,查看当前数据库的 OID 和表空间位置
create database mydb1;
select oid,datname from pg_database;


2.创建测试表test_vm_fsm,找到表在OS层文件对应位置
在新建表时不会产生fsm和vm文件,直到pg执行VACUUM操作时,或者第一次使用 fsm或vm 文件时才会生成。
create table test_vm_fsm (id int,name varchar(100));
SELECT oid, relname, relpages, relallvisible
FROM pg_class
WHERE relname = 'test_vm_fsm';

![]()
3.插入一万行数据
INSERT INTO test_vm_fsm (id, name)
SELECT
generate_series(1, 100000),
'test_' || md5(random()::text);

4.查看对应的vm、fsm文件生成
SELECT
relname 表名,
n_live_tup 存活行数,
n_dead_tup 死元组行数,
pg_size_pretty(pg_total_relation_size(relid)) 总占用空间
FROM pg_stat_user_tables
WHERE relname = 'test_vm_fsm';


*5.删除部分数据,再查看vm统计信息、查看fsm块状态
删除
DELETE FROM test_vm_fsm
WHERE id IN (
SELECT id FROM test_vm_fsm
ORDER BY random()
LIMIT 1000
);
SELECT oid, relname, relpages, relallvisible
FROM pg_class
WHERE relname = 'test_vm_fsm';


VM
relpages = 表占用的总数据块数,relallvisible 是 VM 文件标记为全部可见的块数

删除数据后vm/fsm文件大小在OS层无变化,表示删除数据仅把行标记为无效,依然占用空间

FSM
安装PG扩展,查看FSM内部信息
CREATE EXTENSION pg_freespacemap;
CREATE EXTENSION pg_visibility;
SELECT * FROM pg_available_extensions WHERE name = 'pg_freespacemap';
SELECT * FROM pg_available_extensions WHERE name = 'pg_visibility';
SELECT blkno, avail
FROM pg_freespace('test_vm_fsm'::regclass)
ORDER BY avail
LIMIT 10;

blkno:数据块号(PG 默认每个块 8KB = 8192 字节)
aiail:块中还剩多少字节
可以看出每个8kb的块剩余32字节,几乎写满,删除只是标记删除,所以几乎没有空闲空间


6.进行vacuum,查询*5的命令
VACUUM test_vm_fsm;
SELECT
relname 表名,
n_live_tup 存活行数,
n_dead_tup 死元组行数,
pg_size_pretty(pg_total_relation_size(relid)) 总占用空间
FROM pg_stat_user_tables
WHERE relname = 'test_vm_fsm';
SELECT blkno, avail
FROM pg_freespace('test_vm_fsm'::regclass)
ORDER BY blkno
LIMIT 10;
vacuum后死元组已被清理,已删除部分空间成功释放,OS层文件大小不变



更多推荐
所有评论(0)