PostgreSQL数据库表膨胀的一些原因

2024/11/14 16:10
阅读数 49

本文为墨天轮数据库管理服务团队原创内容,如需转载请联系小墨(VX:modb666)并注明来源。

作者:阎书利

墨天轮数据库管理服务PG技术顾问

PostgreSQL ACE,曾就职于电信、新能源等行业,目前主要从事PG和PG系开源数据库技术支持以及去O的相关工作。熟悉PostgreSQL、Oracle以及opengauss/MogDB等国产数据库。

PostgreSQL表膨胀的原因主要有两个:一个是垃圾数据,即dead tuple行数太多未及时清理,导致不能及时提供能重用的空间,二是数据页之间存在空闲空间。

01

表的填充因子设置

表的填充因子是个很神奇的东西,因为设置太大或者太小,都可能因不同原因引起表膨胀。
  • 较低的填充因子(例如 70%)意味着每个数据页中会留出 30% 的空闲空间。这样可以减少页面因更新而频繁分裂的可能性。这通常会导致表的实际磁盘使用量增加,因为每个页面上的有效数据量较少。所以造成表的膨胀。
  • 而表的 fillfactor 设置也可能会影响 VACUUM 的效果。如果 fillfactor 设置得过高,会导致表在插入新行时未能有效利用空间,增加了死元组的存在。(默认100)。
PostgreSQL 的表默认填充因子为 100,这意味着默认情况下,PostgreSQL 会尽量将每个页面填满数据,不留空间给未来的数据更新。假设一张表的填充因子设定为 70%。这样在 PostgreSQL 插入数据到页面时,会故意留下 30% 的空间空着,以便未来对现有数据行的更新。当表的填充因子更高(接近 100%)时,每个数据页面的空余空间更少,这可能导致数据行更新时空间不足,需要重新分配页面。

   
   
   
postgres=# alter table t1 set (fillfactor = 70);
ALTER TABLE

postgres=# SELECT
relname AS table_name,
reloptions
FROM
pg_class
WHERE
relname = 't1';
+------------+-----------------+
| table_name | reloptions |
+------------+-----------------+
| t1 | {fillfactor=70} |
+------------+-----------------+
(1 row)

02

VACUUM和VACUUM FULL本身机制

VACUUM有时候只是标记了空间为可用,但磁盘空间可能不会立即反映出来。

03

AUTOVACUUM参数不合理死元组不能

及时清理

调整 autovacuum 配置参数,特别是 autovacuum_vacuum_scale_factor 和 autovacuum_vacuum_threshold。例如,降低 autovacuum_vacuum_scale_factor 的值,可以让 autovacuum 更频繁地运行。

04

一些原因导致vacuum后没清理死元组

主要有如下几种方式
  1. 失效复制槽
  2. 长事务导致
  3. 存在未提交的prepare事务
  4. idle in transaction状态的事务
  5. 函数等内部结构涉及到表的访问
  6. hot_standby_feedback参数问题
  7. 索引状态问题
  8. 表和索引的并发访问

可以参考这篇文章:

https://www.modb.pro/db/1834221798582394880

05

表的统计信息问题

如果表的统计信息有问题,可能会影响 VACUUM 的效果。

06

maintenance_work_mem参数设置太小

死元组不能及时清理

maintenance_work_mem 设置得太低可能会限制 VACUUM 的效率,因为VACUUM过程需要在maintenance_work_mem里缓存dead tuple的tupleid,如果太小则可能分多次清理,每次在maintenance_work_mem缓存满之后就会触发一次清理,除非之外还可能涉及到多次扫描索引的问题。 正常情况下,每满一次,就会重新扫描一遍所有的索引。
v11-v13引入的一个GUC参数vacuum_cleanup_index_scale_factor,但是在v14取消了,对于大量insert,没有update、delete操作的表的vacuum,或者常规静态表的vacuum会快很多,因为不需要scan index了。
而v12版本也在表级别增加了vacuum_index_cleanup参数,可以在创建表的时候设置,也可以alter table设置。参数可以控制VACUUM在是否禁用索引清理的情况下运行,默认值为true。v12版本的VACUUM引入了一个新的选项INDEX_CLEANUP,可以跳过索引的垃圾回收。
除此之外PostgreSQL-17版本之前,maintenance_work_mem虽然可以设置很大,但是对于vacuum本身的使用,有一个1GB的最大值限制,也就是不管你设置的多大,最多一个vacuum能用到的最多也就1GB。这个在PostgreSQL数据库的文档里也有相应的记录。从PostgreSQL-17版本取消了这个限制。

tupleid为6字节长度。1GiB可存储1.7亿条左右dead tuple的tupleid。默认垃圾记录约等于表大小的20%时触发垃圾回收, 8.9亿条记录的表20%的垃圾即1.7亿条dead tuple,因此超过8.9亿, 该表的垃圾回收就可能要多次扫描index了。所以以这个方向来看的话,PostgreSQL单表不建议超过8.9亿条记录。

07

磁盘性能问题导致死元组不能及时清理

磁盘 I/O 性能问题或数据文件系统的配置也可能影响 VACUUM 的效果,如果磁盘性能不好,可能VACUUM的效率比较低,死元组不能及时清理。可以使用fio、iostat 或 vmstat来检查磁盘IO情况。

08

PostgreSQL 的事务 ID (XID) Wraparound

PostgreSQL 使用事务 ID (XID) 来追踪事务。长时间运行的事务或频繁的事务生成可能导致 XID Wraparound 问题,这会影响 VACUUM 的效果。如果 XID 接近其最大值,数据库会进行 VACUUM 来防止 XID Wraparound,但这个过程可能不会完全清理死元组。

09

表的特定数据类型影响VACUUM

某些数据类型(例如数组类型或自定义数据类型)可能会影响 VACUUM 的行为,尤其是在处理复杂的数据结构时。

10

是否误关闭了autovacuum的选项,导致不能及时清理



THE END

墨天轮从乐知乐享的数据库技术社区蓄势出发,全面升级,提供多类型数据库管理服务。墨天轮数据库管理服务旨在为用户构建信赖可托付的数据库环境,并为数据库厂商提供中立的生态支持。

服务官网:https://www.modb.pro/service



点击进入作者个人主页

本文分享自微信公众号 - 墨天轮(enmocs)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部