暂无图片
暂无图片
2
暂无图片
暂无图片
1
暂无图片

vacuum full优化--支持空间预检查

原创 杨向博 2024-09-10
305

前言

如果没有合理的业务设计,vacuum full或将是PG用户一个无法绕开的痛点。特别是对于DBA而言,可能会带来不小的困扰。
正如我在“PostgreSQL数据库技术峰会西安站”中分享的一样,当我们处理大表vacuum full时,由于一些客观因素,磁盘监控告警没有达到预期,很可能会出现vacuum full写满磁盘的情况,这个时候大概率要背锅。

因此,在我看来内核自身支持vacuum full空间预检查是个不错的主意。

vacuum full原理

在设计方案之前,我们回顾下为什么PG中需要vacuum full?

借用下interdb对应章节中的相关内容:

image.png

标记删除特性,vacuum并不释放空间,当vacuum效率低于dead_tuple产生速率就会出现“表膨胀”,造成空间浪费同时影响性能。这个时候就需要vacuum full来处理,当然也可以使用各种扩展,选择vacuum full是因为它足够“简单”。

image.png

vacuum full原理简介
1)创建新表
2)将live tuples数据copy到新表
3)清理old file,更新FSM和VM

vacuum full主要流程

image.png

方案设计

image.png
1)设计一个guc参数控制是否开启预检查特性
2)计算表总大小(包含索引、toast),获取表所在表空间物理路径(对于软链接的处理,返回目标链接所在路径)这里可以照猫画虎新增c函数(参考sql函数,修改入参方式和返回值等,总之微调)或者是SPI方式去调用这些sql函数。我选择了前者。
3)当表大小大于对应分区剩余空间大小时报错终止vacuum full

逻辑看起来很简单,那么这段逻辑放在什么位置合适呢?
放在vacuum加AccessExclusiveLock之后的话,计算表大小时耗时相对久一点,加锁时间会久;放在加锁之前表大小可能计算不准确?

好吧,我暂且先放在加锁前吧。

代码修改

总览:涉及6个文件,几百行。
image.png

主要逻辑:

/* * Begin at 2024-07-24 * Add by NickYoung * check the free space of tablespace's file system, when we run vacuum full or cluster (table) command */ if (vacuum_full_check_space) { char *filepath; char *filepatha; char *filepathb; int64 tablesize; struct config_generic *record; const char *name = "data_directory"; char *pgdata; char *tbspath; char *tbsoidstr; Oid tbsoid; char *tabfilepath; char *tbsfs; char *tbsfslink; struct statvfs stat; unsigned long totalspace; unsigned long freespace; /* Get the relative path of the table file from the pgdata directory */ filepath = pg_rel_filepath(tableOid); filepatha = pstrdup(filepath); filepathb = pstrdup(filepath); /* Calculate table size */ tablesize = pg_total_rel_size(tableOid); /* Get data_directory configuration */ record = find_option(name, false, false, ERROR); pgdata = ShowGUCOption(record, true); tbspath = subpathstring(filepatha,1); tbsoidstr = subpathstring(filepathb,2); tbsoid = atoi(tbsoidstr); /* If the table in a custom tablespace */ if (strcmp(tbspath,"pg_tblspc") == 0) { tbsfs = pg_tbs_location(tbsoid); tbsfslink = pg_dir_islink(tbsfs); } /* If the table in pg_global tablespace */ else if (strcmp(tbspath,"global") == 0) { tbsfs = psprintf("%s/%s",pgdata,"global"); tbsfslink = pg_dir_islink(tbsfs); } /* If the table in pg_default tablespace */ else { tbsfs = pg_dir_islink(pgdata); /* If the PGDATA directory is not a link, check PGDATA/base directory */ if (strcmp(pgdata,tbsfs) == 0) { tabfilepath = psprintf("%s/%s",pgdata,"base"); tbsfs = pg_dir_islink(tabfilepath); /* If the PGDATA/base directory is not a link, check PGDATA/base/databaseOid directory */ if (strcmp(tabfilepath,tbsfs) == 0) { tabfilepath = psprintf("%s/%s",tabfilepath,tbsoidstr); tbsfs = pg_dir_islink(tabfilepath); } } tbsfslink = pstrdup(tbsfs); } /* check the free space of tablespace on it's file system */ if (statvfs(tbsfslink, &stat) != 0) { ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("call statvfs() failed"))); } totalspace = stat.f_blocks * stat.f_frsize; freespace = stat.f_bfree * stat.f_frsize; /* if freespace less than tablesize then report the error */ if (freespace <= tablesize) { ereport(ERROR, (errcode(ERRCODE_DISK_FULL), errmsg("check failed! The free space on the device is not enough.\n" "\ttablesize:\"%ld B\" is bigger than tbsfs:\"%s\" freespace:\"%ld B\"", tablesize,tbsfslink,freespace))); } else { ereport(DEBUG1, errmsg("check passed. The free space on the device is enough.\n" "\ttbsfs:\"%s\" freespace:\"%ld B\" is bigger than tablesize:\"%ld B\" ", tbsfslink,freespace,tablesize)); } } /* * End at 2024-07-24 AM */ /* * We grab exclusive access to the target rel and index for the duration * of the transaction. (This is redundant for the single-transaction * case, since cluster() already did it.) The index lock is taken inside * check_index_is_clusterable. */ OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
复制

测试验证

场景一:磁盘空间充足
image.png
关闭参数时不进行空间预检查。

image.png

打开参数时,可以看到有打印check passed. The free space on the device is enough.

场景二:磁盘空间不足
image.png
表大小13657137152B,表所在路径为软连接/baselink/base,对应/分区可用空间为12631183360B
因此报错可用空间不足,终止vacuum full。

小结

支持了磁盘预检查逻辑,即便监控告警失效也不用担心vacuum full会写满磁盘。这样能很大程度减轻DBA的压力,执行vacuum full不用时刻关注磁盘空间,减少背锅。

当然也期待PG后续可以对文件存储进一步深入优化,例如大文件段的管理,更智能的空间管理等。

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

评论

锁钥
暂无图片
7月前
评论
暂无图片 0
vacuum full优化--支持空间预检查
7月前
暂无图片 点赞
评论