PostgreSQL 垃圾回收参数优化之 - maintenance_work_mem , autovacuum_work_mem

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: PostgreSQL 垃圾回收参数优化之 - maintenance_work_mem , autovacuum_work_mem

背景

夜谈PostgreSQL 垃圾回收参数优化之 - maintenance_work_mem , autovacuum_work_mem。

http://www.postgres.cn/v2/news/viewone/1/398

https://rhaas.blogspot.com/2019/01/how-much-maintenanceworkmem-do-i-need.html

9.4以前的版本,垃圾回收相关的内存参数maintenance_work_mem,9.4以及以后的版本为autovacuum_work_mem,如果没有设置autovacuum_work_mem,则使用maintenance_work_mem的设置。

这个参数设置的是内存大小有什么用呢?

这部分内存被用于记录垃圾tupleid,vacuum进程在进行表扫描时,当扫描到的垃圾记录ID占满了整个内存(autovacuum_work_mem或maintenance_work_mem),那么会停止扫描表,开始INDEX的扫描。

扫描INDEX时,清理索引中的哪些tuple,实际上是从刚才内存中记录的这些tupleid来进行匹配。

当所有索引都扫描并清理了一遍后,继续从刚才的位点开始扫描表。

过程如下:

1、palloc autovacuum_work_mem memory  
  
2、scan table,   
  
3、dead tuple's tupleid write to autovacuum_work_mem  
  
4、when autovacuum_work_mem full (with dead tuples can vacuum)  
  
5、record table scan offset.  
  
6、scan indexs  
  
7、vacuum index's dead tuple (these: index item's ctid in autovacuum_work_mem)  
  
8、scan indexs end  
  
9、continue scan table with prev's offset  
  
...  

显然,如果垃圾回收时autovacuum_work_mem太小,INDEX会被多次扫描,浪费资源,时间。

palloc autovacuum_work_mem memory 这部分内存是使用时分配,并不是直接全部使用掉maintenance_work_mem或autovacuum_work_mem设置的内存,PG代码中做了优化限制:

对于小表,可能申请少量内存,算法请参考如下代码(对于小表,申请的内存数会是保障可记录下整表的tupleid的内存数(当maintenance_work_mem或autovacuum_work_mem设置的内存大于这个值时))。

我已经在如下代码中进行了标注:

/*  
 * MaxHeapTuplesPerPage is an upper bound on the number of tuples that can  
 * fit on one heap page.  (Note that indexes could have more, because they  
 * use a smaller tuple header.)  We arrive at the divisor because each tuple  
 * must be maxaligned, and it must have an associated item pointer.  
 *  
 * Note: with HOT, there could theoretically be more line pointers (not actual  
 * tuples) than this on a heap page.  However we constrain the number of line  
 * pointers to this anyway, to avoid excessive line-pointer bloat and not  
 * require increases in the size of work arrays.  
 */  
#define MaxHeapTuplesPerPage    \  
        ((int) ((BLCKSZ - SizeOfPageHeaderData) / \  
                        (MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))  
  
  
/*  
 * Guesstimation of number of dead tuples per page.  This is used to  
 * provide an upper limit to memory allocated when vacuuming small  
 * tables.  
 */  
#define LAZY_ALLOC_TUPLES               MaxHeapTuplesPerPage  
  
  
/*  
 * lazy_space_alloc - space allocation decisions for lazy vacuum  
 *  
 * See the comments at the head of this file for rationale.  
 */  
static void  
lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks)  
{  
        long            maxtuples;  
        int                     vac_work_mem = IsAutoVacuumWorkerProcess() &&  
        autovacuum_work_mem != -1 ?  
        autovacuum_work_mem : maintenance_work_mem;  
  
        if (vacrelstats->hasindex)  
        {  
                maxtuples = (vac_work_mem * 1024L) / sizeof(ItemPointerData);  
                maxtuples = Min(maxtuples, INT_MAX);  
                maxtuples = Min(maxtuples, MaxAllocSize / sizeof(ItemPointerData));  
  
                /* curious coding here to ensure the multiplication can't overflow */  
  
                这里保证了maintenance_work_mem或autovacuum_work_mem不会直接被使用光,  
                如果是小表,会palloc少量memory。  
  
                if ((BlockNumber) (maxtuples / LAZY_ALLOC_TUPLES) > relblocks)  
                        maxtuples = relblocks * LAZY_ALLOC_TUPLES;  
  
                /* stay sane if small maintenance_work_mem */  
                maxtuples = Max(maxtuples, MaxHeapTuplesPerPage);  
        }  
        else  
        {  
                maxtuples = MaxHeapTuplesPerPage;  
        }  
  
        vacrelstats->num_dead_tuples = 0;  
        vacrelstats->max_dead_tuples = (int) maxtuples;  
        vacrelstats->dead_tuples = (ItemPointer)  
                palloc(maxtuples * sizeof(ItemPointerData));  
}  

maintenance_work_mem这个内存还有一个用途,创建索引时,maintenance_work_mem控制系统在构建索引时将使用的最大内存量。为了构建一个B树索引,必须对输入的数据进行排序,如果要排序的数据在maintenance_work_mem设定的内存中放置不下,它将会溢出到磁盘中。

例子

如何计算适合的内存大小

postgres=# show autovacuum_work_mem ;  
 autovacuum_work_mem   
---------------------  
 1GB  
(1 row)  
  
postgres=# show maintenance_work_mem ;  
 maintenance_work_mem   
----------------------  
 1GB  
(1 row)  

也就是说,最多有1GB的内存,用于记录一次vacuum时,一次性可存储的垃圾tuple的tupleid。

tupleid为6字节长度。

  
/*  
 * ItemPointer:  
 *  
 * This is a pointer to an item within a disk page of a known file  
 * (for example, a cross-link from an index to its parent table).  
 * blkid tells us which block, posid tells us which entry in the linp  
 * (ItemIdData) array we want.  
 *  
 * Note: because there is an item pointer in each tuple header and index  
 * tuple header on disk, it's very important not to waste space with  
 * structure padding bytes.  The struct is designed to be six bytes long  
 * (it contains three int16 fields) but a few compilers will pad it to  
 * eight bytes unless coerced.  We apply appropriate persuasion where  
 * possible.  If your compiler can't be made to play along, you'll waste  
 * lots of space.  
 */  
typedef struct ItemPointerData  
{  
        BlockIdData ip_blkid;  
        OffsetNumber ip_posid;  
}  

1G可存储1.7亿条dead tuple的tupleid。

postgres=# select 1024*1024*1024/6;  
 ?column?    
-----------  
 178956970  
(1 row)  

而自动垃圾回收是在什么条件下触发的呢?

src/backend/postmaster/autovacuum.c

 * A table needs to be vacuumed if the number of dead tuples exceeds a  
 * threshold.  This threshold is calculated as  
 *  
 * threshold = vac_base_thresh + vac_scale_factor * reltuples  
  
vac_base_thresh:   autovacuum_vacuum_threshold    
  
vac_scale_factor:  autovacuum_vacuum_scale_factor    
  
  
postgres=# show autovacuum_vacuum_threshold ;  
 autovacuum_vacuum_threshold   
-----------------------------  
 50  
(1 row)  
  
postgres=# show autovacuum_vacuum_scale_factor ;  
 autovacuum_vacuum_scale_factor   
--------------------------------  
 0.2  
(1 row)  

以上设置,表示当垃圾记录数达到50+表大小乘以0.2时,会触发垃圾回收。

可以看成,垃圾记录约等于表大小的20%,触发垃圾回收。

那么1G能存下多大表的垃圾呢?约8.9亿条记录的表。

postgres=# select 1024*1024*1024/6/0.2;  
      ?column?        
--------------------  
 894784850  
(1 row)  

压力测试例子

postgres=# show log_autovacuum_min_duration ;  
 log_autovacuum_min_duration   
-----------------------------  
 0  
(1 row)  
create table test(id int primary key, c1 int, c2 int, c3 int);  
create index idx_test_1 on test (c1);  
create index idx_test_2 on test (c2);  
create index idx_test_3 on test (c3);  
vi test.sql  
\set id random(1,10000000)  
insert into test values (:id,random()*100, random()*100,random()*100) on conflict (id) do update set c1=excluded.c1, c2=excluded.c2,c3=excluded.c3;  
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 32 -j 32 -T 1200  

垃圾回收记录

2019-02-26 22:51:50.323 CST,,,35632,,5c755284.8b30,1,,2019-02-26 22:51:48 CST,36/22,0,LOG,00000,"automatic vacuum of table ""postgres.public.test"": index scans: 1  
pages: 0 removed, 6312 remain, 2 skipped due to pins, 0 skipped frozen  
tuples: 4631 removed, 1158251 remain, 1523 are dead but not yet removable, oldest xmin: 1262982800  
buffer usage: 39523 hits, 1 misses, 1 dirtied  
avg read rate: 0.004 MB/s, avg write rate: 0.004 MB/s  
system usage: CPU: user: 1.66 s, system: 0.10 s, elapsed: 1.86 s",,,,,,,,"lazy_vacuum_rel, vacuumlazy.c:407",""  
2019-02-26 22:51:50.566 CST,,,35632,,5c755284.8b30,2,,2019-02-26 22:51:48 CST,36/23,1263417553,LOG,00000,"automatic analyze of table ""postgres.public.test"" system usage: CPU: user: 0.16 s, system: 0.04 s, elapsed: 0.24 s",,,,,,,,"do_analyze_rel, analyze.c:722",""  

index scans:1 表示垃圾回收的表有索引,并且索引只扫描了一次。

说明autovacuum_work_mem足够大,没有出现vacuum时装不下垃圾dead tuple tupleid的情况。

小结

建议:

1、log_autovacuum_min_duration=0,表示记录所有autovacuum的统计信息。

2、autovacuum_vacuum_scale_factor=0.01,表示1%的垃圾时,触发自动垃圾回收。

3、autovacuum_work_mem,视情况定,确保不出现垃圾回收时多次INDEX SCAN.

4、如果发现垃圾回收统计信息中出现了index scans: 超过1的情况,说明:

4.1、需要增加autovacuum_work_mem,增加多少呢?增加到当前autovacuum_work_mem乘以index scans即可。

4.2、或者调低autovacuum_vacuum_scale_factor到当前值除以index scans即可,让autovacuum尽可能早的进行垃圾回收。

参考

http://www.postgres.cn/v2/news/viewone/1/398

https://rhaas.blogspot.com/2019/01/how-much-maintenanceworkmem-do-i-need.html

《PostgreSQL 11 参数模板 - 珍藏级》

PostgreSQL 许愿链接

您的愿望将传达给PG kernel hacker、数据库厂商等, 帮助提高数据库产品质量和功能, 说不定下一个PG版本就有您提出的功能点. 针对非常好的提议,奖励限量版PG文化衫、纪念品、贴纸、PG热门书籍等,奖品丰富,快来许愿。开不开森.

9.9元购买3个月阿里云RDS PostgreSQL实例

PostgreSQL 解决方案集合

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
1月前
|
DataWorks Java 关系型数据库
DataWorks常见问题之将预警信息发送至邮箱
DataWorks是阿里云提供的一站式大数据开发与管理平台,支持数据集成、数据开发、数据治理等功能;在本汇总中,我们梳理了DataWorks产品在使用过程中经常遇到的问题及解答,以助用户在数据处理和分析工作中提高效率,降低难度。
34 1
|
6月前
|
SQL 关系型数据库 测试技术
沉浸式学习PostgreSQL|PolarDB 20: 学习成为数据库大师级别的优化技能
在上一个实验《沉浸式学习PostgreSQL|PolarDB 19: 体验最流行的开源企业ERP软件 odoo》 中, 学习了如何部署odoo和polardb|pg. 由于ODOO是非常复杂的ERP软件, 对于关系数据库的挑战也非常大, 所以通过odoo业务可以更快速提升同学的数据库优化能力, 发现业务对数据库的使用问题(如索引、事务对锁的运用逻辑问题), 数据库的代码缺陷, 参数或环境配置问题, 系统瓶颈等.
762 1
|
5月前
|
缓存 关系型数据库 数据库
PostgreSQL技术大讲堂 - 第32讲:数据库参数调整
从零开始学PostgreSQL技术大讲堂 - 第32讲:数据库参数调整
446 2
|
4月前
|
SQL 关系型数据库 数据库
postgresql数据库修改参数的方式
在PostgreSQL数据库中,你可以通过多种方式修改数据库参数,以更改其行为。以下是一些常见的修改数据库参数的方式: 1. **通过配置文件修改(postgresql.conf):** PostgreSQL的配置文件是 `postgresql.conf`。你可以直接编辑该文件,找到要修改的参数,修改其值,然后重新启动PostgreSQL服务以使更改生效。 通常,`postgresql.conf` 文件位于 PostgreSQL 数据目录下。修改完毕后,确保重新启动 PostgreSQL 服务。 2. **使用 ALTER SYSTEM 命令:** PostgreSQL
101 1
|
17天前
|
存储 JSON 关系型数据库
PostgreSQL Json应用场景介绍和Shared Detoast优化
PostgreSQL Json应用场景介绍和Shared Detoast优化
|
2月前
|
关系型数据库 Java 分布式数据库
PolarDB for PostgreSQL参数问题之参数删除失败如何解决
PolarDB for PostgreSQL是基于PostgreSQL开发的一款云原生关系型数据库服务,它提供了高性能、高可用性和弹性扩展的特性;本合集将围绕PolarDB(pg)的部署、管理和优化提供指导,以及常见问题的排查和解决办法。
|
3月前
|
弹性计算 关系型数据库 数据库
开源PostgreSQL在倚天ECS上的最佳优化实践
本文基于倚天ECS硬件平台,以自顶向下的方式从上层应用、到基础软件,再到底层芯片硬件,通过应用与芯片的硬件特性的亲和性分析,实现PostgreSQL与倚天芯片软硬协同的深度优化,充分使能倚天硬件性能,帮助开源PostgreSQL应用实现性能提升。
|
3月前
|
关系型数据库 PostgreSQL
PostgreSQL 的哪些参数不能通过ALTER SYSTEM SET 修改
在 PostgreSQL 中,有一些参数是不能通过 `ALTER SYSTEM SET` 语句进行动态修改的,这些参数通常需要在 PostgreSQL 的配置文件中进行手动修改。以下是一些不能通过 `ALTER SYSTEM SET` 修改的常见参数: 1. **track_activities** 2. **track_counts** 3. **track_io_timing** 4. **track_functions** 5. **track_activity_query_size** 6. **track_commit_timestamp** 7. **shared_preload
|
7月前
|
SQL 弹性计算 测试技术
如何在PolarDB-X中优化慢SQL
《PolarDB-X动手实践》系列第六期,本场景带您体验如何使用PolarDB-X提供的解决慢SQL的相关工具。
733 0
|
8月前
|
存储 Java 测试技术
深度优化 | PolarDB-X 基于向量化SIMD指令的探索
本文将介绍PolarDB-X对于向量化SIMD指令的探索和实践,包括基本用法及实现原理,以及在具体算子实现中的思考和沉淀。

相关产品

  • 云原生数据库 PolarDB