PostgreSQL 2023 Cc 大会 美女讲 index 维护,膨胀 与 vacuum

2023-10-24 19:40:21 浏览数 (3)

每天感悟

代码语言:javascript复制
如果你是孙猴,必然你身后有一个如来,如果你是如来,必然你身后有弥勒佛,如果你是什么都无所谓,因为你身后都站着一个有所谓,任何时间,任何地点,都清楚的意识,你不过就是一个你而已。

本期带来的是题目是《管理你元组的坟地》,带来这个话题的是Chelsea,她服务于一家互联网的金融公司,负责以下的工作范围,参加下图,在此之前他是一个后端的开发工程师,现在他是数据管理团队的Team leader

在介绍中他们拥有上百个 PostgreSQL 的数据库,并且她还负责管理相关的基础设施等,并且帮助团队来优化他们的SQL。今天她带来的话题主要有四个,什么是MVCC, 表膨胀,有效的避免表膨胀与整改,设计出对于膨胀有预知功能的数据访问的模式。这些表膨胀的问题怎么产生的,然后我们进行扩展性的讨论怎么化解这些问题,pg_repack, pgstattuple,以及自动真空配置,最后我们讨论一下膨胀的感知,数据访问模式和架构等部分,尽量避免我们提到的上面的问题。

下面开始第一个部分 MVCC,实际上MVCC 要解决的问题看似是矛盾的,MVCC 是要解决事务的隔离性,同时还要解决数据访问的中的性能部分。

在事务的特性中遵循着ACID 四个特性,其中I 就是我们要说的隔离性,这里的I 是数据库的基本功能和数据库的事务的定义,这里的隔离是在整个事务运行的过程中,并且不论多长时间的事务隔离性都能掌控相关的表,而另一边是希望有更加好的性能,在访问表的层面上,读不影响写,写不影响度,通过这个部分来实现快速访问的性能的实现。

但是这些本身是矛盾的,因为要完成事务的隔离就需要使用数据库的锁,而使用数据库的锁中一种简单的实现方式级联锁,他们互相牵制,导致性能不佳,如果读锁需要等待写锁,写锁需要等待读锁,则在现代的数据库比如POSTGRESQL 是不能被接受的,因为他同时有几十上百的访问在通过数据库获取数据。

MVCC 采用了一种数据版本的方式来管理,基本上实现了通过元组来管理行版本。元组实际上就是行,通过这些元组在表中来表达数据随着时间变化的过程,这里我们要讨论的元组分为两类,live tuple , dead tuple,这里的活的元组的意思是,目前的行版本或旧的行版本,但还在被事务正在使用的版本。举例你有一个大的查询30分钟,在这段时间里面行的数据变化了,但是你的旧的行版本仍然有必要存在,30分钟的查询旧的行版本是作为一个实时的元组存在的。死的元组是在任何正在运行的事务中都不存在意义的行,在我们实际中的MVCC 是通过快照的方式来实现,快照是一个内部的内存数据结构,以每笔postgresql 交易为基础。

他使用如XMIN, XMAX,xip_list 来确定什么事务可以看到什么元组,大部分都是用xmin xmax 来实现的,xmin 是交易插入元组的ID,xmax 是交易ID,如果发生了删除或更新,则会产生XMAX,如果没有就只有XMIN,xip_list,主要是记录正在是使用的行列表。(后面用一个实例来讲了行插入,行的删除,这里略过)。

但是行不能一直增加为了解决这个问题,我们使用了真空vacuum,vaccum有很多的功能,但今天我们要关注的是第一条,删除元组释放空间并可以让空间进行重用,除此以外他还负责更新系统表数据,分析提高查询分析器的有效性等,并且更新可见性的map (VM文件),这有助于真空和仅仅索引扫描工作的性能等,同时还有回收事务ID,方式发生事务ID冻结,或事务ID 的wrap.

PostgreSQL中的页面大小为8KB,页面创建后是不会删除的,真空将删除页面中的元组,但不是页面本身

现在我们来讲讲第二部分,表膨胀的部分

下图中展示了同样的数据,存储在不同情况下,占用的页面的情况,上面只使用了3个页面,下面使用7个页面,这里我要表达的是,这就是表膨胀实际的表现形式,在实际中虽然每页有空余的地方,但是还是会申请新的页面进行处理,同时下面的图形也会给我们一个提醒,在读取数据的情况下,上面的数据存储在页面的情况,必然比下面的情况扫描消耗的IO 要低,即使是进行了vacuum 的操作。

这里看上去膨胀本身就会产生一些问题,膨胀本身就像一个核心的问题,是产生其他问题的根本原因,他会增加读取延迟,读取错误,增加IO消耗,磁盘使用率高昂,在某些托管服务中如果按照 IOPS 来进行收费,则可能会付出更多的费用。

膨胀的产生于繁重的更新操作,删除操作等,或者进行一次性大量的批处理的操作中产生大量的更新行等,同时autovacuum的不及时,相关的参数调整的比较保守,导致回收不及时,这些都是导致膨胀的原因。

第三部分我们说说如何量化减轻问题,或避免问题

量化的手段可以分为两类,1 使用Postgresql 的扩展,pgstattuple 来返回精确的值,多少页面在使用,或用于活动或实时的元组或死的元组,将其转换为百分比返回,通过这样的方式获得的数据会比较慢,因为他需要每个页面都进行相关的扫描,或者使用pg_class里面的对于表的数据进行评估的方式,很多在线上的查询使用的成本评估的方式。

如果你使用的是第一种方式pgstattuple的方法,他会返回如下图的信息,包含表长度(以字节为单位),LIVE 元组的占比,以及dead 元组的占比。

另一种方式,你对表进行analyze 然后运行一个查询,如下图得到了表的bytes ,膨胀的比率,当然相信你是一个优秀的DBA,会写出这样的SQL。

下面我不想展开关于计算的方法,具体的可以去看另一个演讲者相关的对于这部分的PPT。

下一个问题是,这里需要评估到底多大的表膨胀算严重的问题,这其实是一个很恼人的问题,这实际上是依赖与你自己的业务系统以及数据库系统的定义,我这里根据我自己的工作经验,这里如果是非常小的数据库,非常小的表,那么70%的膨胀率都是可以接受的,他不会影响其他的,而1-30G 的数据库表可以接受的 25%的膨胀率,更大的表,30-100G 则20%是可以接受的,当然更大的表18% 或许可以被接受。

在处理膨胀这个问题需要注意两点,autovacuum 设置后进行更多的aggressive 的工作,以及对于表进行repack 的操作,重建是针对你的表真的很臃肿的情况下。

你要做的第一件事情是要对于你的autovacuum的参数进行调整,默认autovacuum_vacuum_scale_factor 是0.2 ,这意味着你的表多大都要等到据上次触发autovacuum 后表在变化20%后才能再次触发autovacuum。如果你将这个参数改变为0.01 则意味着,会更加频繁的进行autovacuum,实际行还有另一个参数 autovacuum_vacuum_threshold ,这两个参数是各玩各的,我通常都是将 autovacuum_vacuum_scale_factor设置为0 ,单独设置autovacuum_vacuum_threshold 来进行触发autovacuum。这样设置的好处是,触发点是据你上次进行vacuum后具体的数字再次触发。其次你可以调整autovacuum_vacuum_cost_delay

这里有两个版本PG11 和之前的版本,这里默认是20ms,而现在默认是2ms,对于现代的硬件产品可以适配2ms.

另一个参数autovacuum_max_workers ,数千个表在一台数据库服务器上,我建议你去看pg_stat_progress_vacuum 当前有多少个真空在一个给定的时间运行。如果在查看后,每次都是用尽你的vacuum workers,我们我建议你考虑,逐个向上调节你的workers的数量。另外你可以采用重建或重新repack的方式来进行臃肿表的处理。这里vacuum full采用了非常重的锁,他将你的表和索引都进行重建到新的磁盘空间,并释放老的占用的空间,在操作过程你不能进行任何对表的读写的操作,这是不能被忍受的。

这里我们可以使用pg_repack 或 pg_squeeze 将表进行复制,并通过triggers来进行增量数据的追偿,但这需要两倍的空间,以及良好的内存和CPU 性能。这里我推荐使用这样的方法,因为最坏的情况无非是在repack的情况下失败,然后你必须手动清理临时表但不会丢失数据。

pg_repack 和 pg_squeeze 二者还是有不同的,将二者进行比较作为替代的方案,对于pg_repack你需要安装扩展,并且可以从客户端的外部进行操控,对于数据的二进制部分并没有改变,这对于RDS 之类的部分是可以被接受的,而pg_squeeze实际上是对二进制文件进行更改,但这样的方式不能在RDS 或托管服务上进行使用,因为他对系统有更多的侵入。

最后一部分我们需要考虑关于如何设计来用更好的方式来使用POSTGRESQL ,首先我们进行一个关于数据库的思考,你会对数据库表进行的读写操作的百分比,比如插入,更新或删除等。大约你希望数据的增长量是多少。如果你的设计中有大量的更新,是否可以改变比如从一次更新多行,变为更新一行,减少数据的更新和删除,更新一行多次变为更新一行一次。

如果你有大量的删除操作,你是否可以考虑使用分区表来进行,通过删除分区表的分区来完成同样的工作,而不是在一张表中进行大型的删除作业。你可以考虑减少调整自动真空大小的参数,或许他需要更频繁的触发和一个更小的值。这里我有一个规则,是POSTGRESQL 的事情,他来做,不是他的事情,找该做的对象来做。比如你有全文索引需求25G 可以在POSTGRESQL 上做的很好,但是如果是 100G 则需要找 ES来做,同理如果你是进行KEY VALUE的处理 也是一样,较大的需求应该去找redis来做。

0 人点赞