暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

VACUUM 不会缩小 PostgreSQL 表

原创 肯肯在学习 2022-10-11
427

你有没有想过为什么 VACUUM 不会使你的PostgreSQL表更小?你有没有想过 VACUUM 为什么不收缩数据文件?好吧,也许这就是您一直在寻找的文章。本文重点是:

  • 通常清理后空间不会返回到操作系统,本文介绍了其作用原因,这很重要。
  • 人们经常对 VACUUM 的内部工作原理做了一些错误的假设。深入挖掘并了解到底发生了什么也是有必要的。

以下内容向用户揭示了有关 VACUUM 的最有用的秘密。

了解元组可见性

要了解 PostgreSQL 中的 VACUUM ,重要的是要首先了解 PostgreSQL 如何处理可见性。整个概念基于一组隐藏的列,这些列是行的一部分。以下是它的工作原理:\

test=# CREATE TABLE t_test (id int); CREATE TABLE test=# INSERT INTO t_test VALUES (5), (6), (7); INSERT 0 3 test=# INSERT INTO t_test VALUES (8), (9), (10); INSERT 0 3
复制

为了保持简单,我们刚刚创建的表仅由一列组成。请注意,数据已使用两个单独的事务加载到表中。每个事务都插入了 3 行,这在隐藏列中明确指示:

test=# SELECT ctid, xmin, xmax, cmin, cmax, * FROM t_test; ctid | xmin | xmax | cmin | cmax | id ------+------+------+------+------+---- (0,1) | 764 | 0 | 0 | 0 | 5 (0,2) | 764 | 0 | 0 | 0 | 6 (0,3) | 764 | 0 | 0 | 0 | 7 (0,4) | 765 | 0 | 0 | 0 | 8 (0,5) | 765 | 0 | 0 | 0 | 9 (0,6) | 765 | 0 | 0 | 0 | 10 (6 rows)
复制

xmin, xmax, cmin 和 cmax 是包含事务 ID 的隐藏列。如您所见,前三行已由事务编号 764 写入,而其余数据已使用事务编号 765 创建。

隐藏列将处理可见性,PostgreSQL将(通常但并非总是)根据这些列确定某个事务是否可以看到行。

运行 UPDATE 语句将如下显示:

test=# BEGIN; BEGIN test=*# UPDATE t_test SET id = id * 10 WHERE id > 9 RETURNING *; id ----- 100 (1 row) UPDATE 1 test=*# SELECT ctid, xmin, xmax, cmin, cmax, * FROM t_test; ctid | xmin | xmax | cmin | cmax | id ------+------+------+------+------+----- (0,1) | 764 | 0 | 0 | 0 | 5 (0,2) | 764 | 0 | 0 | 0 | 6 (0,3) | 764 | 0 | 0 | 0 | 7 (0,4) | 765 | 0 | 0 | 0 | 8 (0,5) | 765 | 0 | 0 | 0 | 9 (0,7) | 766 | 0 | 0 | 0 | 100 (6 rows)
复制

已更改一行。但是,让我们将重点放在 CTID 上,它表示磁盘上行的物理位置。请注意,(0, 6) 已消失,因为 PostgreSQL 必须复制该行。如果我们运行第二个 UPDATE ,则该行将再次被复制:

test=*# UPDATE t_test SET id = id * 10 WHERE id > 9 RETURNING *; id ------ 1000 (1 row) UPDATE 1 test=*# UPDATE t_test SET id = id * 10 WHERE id > 9 RETURNING *; id ------- 10000 (1 row) UPDATE 1
复制

复制这些行很重要,因为我们必须保留旧行。否则 ROLLBACK 将不起作用,因此必须保留旧版本。

让我们再次检查该表:

test=*# SELECT ctid, xmin, xmax, cmin, cmax, * FROM t_test; ctid | xmin | xmax | cmin | cmax | id ------+------+------+------+------+------- (0,1) | 764 | 0 | 0 | 0 | 5 (0,2) | 764 | 0 | 0 | 0 | 6 (0,3) | 764 | 0 | 0 | 0 | 7 (0,4) | 765 | 0 | 0 | 0 | 8 (0,5) | 765 | 0 | 0 | 0 | 9 (0,9) | 766 | 0 | 2 | 2 | 10000 (6 rows) test=*# COMMIT; COMMIT
复制

5到9之间的一切都充满了必须删除的死行。

VACUUM :清理行

请记住,COMMIT 也不得杀死死行。因此,清理过程必须异步完成。这正是 VACUUM 的作用。让我们运行它,看看会发生什么:

test=# VACUUM VERBOSE t_test INFO: vacuuming "test.public.t_test" INFO: finished vacuuming "test.public.t_test": index scans: 0 pages: 0 removed, 1 remain, 1 scanned (100.00% of total) tuples: 3 removed, 6 remain, 0 are dead but not yet removable removable cutoff: 767, which was 0 XIDs old when operation ended new relfrozenxid: 764, which is 1 XIDs ahead of previous value index scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removed avg read rate: 22.790 MB/s, avg write rate: 27.348 MB/s buffer usage: 6 hits, 5 misses, 6 dirtied WAL usage: 3 records, 3 full page images, 14224 bytes system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s VACUUM
复制

VACUUM 正在积极寻找不再被任何人看到的行。这些行可以位于数据文件中间的某个位置。发生的事情是 VACUUM允许PostgreSQL重用该空间。但是,它不会将该空间返回给操作系统。它不能这样做,因为如果你有一个大小为1 GB的数据文件,你不能简单地将“文件的中间”返回到操作系统,以防它是空的,也没有文件系统操作能够支持。相反,PostgreSQL必须记住这个可用空间,并在以后重用它。

VACUUM 规则的例外

但是,该规则有一个例外。请考虑以下代码段:

test=# SELECT pg_relation_size('t_test'); pg_relation_size ------------------ 8192 (1 row) test=# DELETE FROM t_test; DELETE 6 test=# SELECT pg_relation_size('t_test'); pg_relation_size ------------------ 8192 (1 row)
复制

即使在 DELETE 语句之后,表仍将保留其大小。请记住:清理是异步完成的。因此,VACUUM 可以调用以删除这些行:

test=# VACUUM t_test; VACUUM
复制

这是一个特例。规则是:如果从表中的某个位置开始,所有行都已死,VACUUM 则可以截断该表。这正是这个例子中发生的事情:

test=# SELECT pg_relation_size('t_test'); pg_relation_size ------------------ 0 (1 row)
复制

毕竟,在大型表中,数据文件末尾总是有几行(在正常情况下)。因此,不要指望 VACUUM 则来缩小表。

VACUUM FULL vs. pg_squeeze

对抗表膨胀(这是用于描述表不成比例增长的技术术语)的一种方法是使用 VACUUM FULL。但是,问题在于 VACUUM FULL 需要一个表锁,如果您处于业务关键型环境中,这可能是一个真正的问题。因此,我们开发了一种无需大量锁定即可重新组织大型表的方法。

该解决方案称为pg_squeeze。从Github我们的网站免费下载。

原文标题:VACUUM DOES NOT SHRINK MY POSTGRESQL TABLE
原文作者: Hans-Jürgen Schönig
原文地址:https://www.cybertec-postgresql.com/en/vacuum-does-not-shrink-my-postgresql-table/

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论