说说PostgreSQL的表膨胀

来源:这里教程网 时间:2026-03-14 21:43:54 作者:

原生的PostgreSQL的特殊之处

Oracle和MySQL都有undo这个机制,原生的PostgreSQL没有这个机制,它有自己独特的实现方式。

实验如下 创建一个简单的表和索引

xxg=# CREATE TABLE AA (id integer, name text); CREATE TABLE xxg=#

xxg=# CREATE INDEX x2 ON AA (id); CREATE INDEX

写入10万行数据,并查看大小

xxg=# INSERT INTO AA (id, name) SELECT i, ‘xxx’ FROM generate_series(1, 100000) AS s(i); INSERT 0 100000

xxg=# SELECT pg_size_pretty(pg_table_size(‘AA’)); pg_size_pretty 3584 kB (1 row)

这个表简单,所以10万行只有3.5M

全表进行一次更新,并查看大小

xxg=# update AA set name=‘yyy’; UPDATE 100000

xxg=# SELECT pg_size_pretty(pg_table_size(‘AA’)); pg_size_pretty 7120 kB (1 row) 可以看到还是10万行数据,存储空间基本是大了一倍7.1M

再一次全表进行更新,并查看大小

xxg=# update AA set name=‘zzz’; UPDATE 100000

xxg=# SELECT pg_size_pretty(pg_table_size(‘AA’)); pg_size_pretty 10 MB (1 row)

可以看到还是10万行数据,存储空间基本是在上次的基础上又加了3M左右。达到了10M

可以想象再一次全表,估计总大小会是13M左右。这就是表膨胀。每次变更后的数据都存下来,变更前的也在。

如何回收?

数据库自然是考虑这个问题有一个方式就是定期回收一下。现在的版本都是自动化去做的。定期执行vacuum table。 vacuum table这个命令可以让后面的数据服用之前的空间。只是可以复用,但是不释放空间。 要彻底释放是要vacuum full table。 xxg=# vacuum aa; VACUUM Time: 2.519 ms xxg=# vacuum aa; VACUUM Time: 1.165 ms xxg=# vacuum full aa; VACUUM Time: 292.392 ms

可以看到执行full的时候比不带full差了有将近300倍的时间。

xxg=# SELECT pg_size_pretty(pg_table_size(‘AA’)); pg_size_pretty

3552 kB (1 row)

Time: 2.121 ms 最后空间回到了3.5M左右。

这个时候可以理解为碎片整理,执行的时候明显慢。那个时候对这个表干什么都阻塞。这种碎片整理的日常都不敢随便。尤其是大表。 我想起10多年前我有一次Oracle 20T的表,1T的碎片。在好的机器上整理碎片 55个小时。另外一个同事io差。执行同样的操作用了550个小时(3周)。整个过程可以继续读写。 而这里执行vacuum full table至少写要受到影响了。

引发思考

以上对于熟悉PG的人来说这都不叫事,但是也有不熟悉的,这是写给不熟悉的人看的。 PG也是和Oracle类似的堆表,MySQL是索引聚集表,那么Oracle有高水位线一说,按说PG也应该有高水位线。具体这个还没有论证。 我们假设有的话,以我国实际情况,就是写出全表SQL,是太容易了。那么在PG这上面就会更加放大,(因为历史的数据把表撑大了,每次全表扫描都在这个基础上)那在PG系的国产上,理论上性能问题不少吧?

相关推荐