PostgreSQL内核原理之vacuum
一、为什么需要VACUUM
我们知道,MVCC是数据库实现并发一致性的重要手段,但是各个数据库在实现MVCC的时候不尽相同,主要差异在于版本链的管理。以Oracle为例的undo派把历史版本单独放在undo段中,数据页上只存放最新版本,相当于是新老数据分开存储,线程回收历史版本时不会对业务产出影响;而以Postgres为例的append派把历史版本和最新版都存放在数据页上,回收老版本需要遍历所有数据页,会产生较高的IO开销。
二、死元组的产生
1. DELETE产生的死元组
如图所示,页面上有3条元组,当执行delete from t1 where ctid='(0, 2)'删除第二条元时,delete操作只是给 Tuple上打一个删除标记,行指针和Tuple本身不会做任何修改,此时Tuple2会一直残留在页面上。
2. UPDATE跨页产生的死元组
前面提到,Postgres是append更新,因此当我们执行update后老元组就会变成死元组,如图所示,当执行update t1 set a=a+1 where ctid='(0, 1)'来更新Tuple1时,新元组Tuple1'被写到了其他页面,这是Tuple1就是页面上的死元组。
3. HOT UPDATE产生的死元组
Postgres中为了提升性能,当满足条件时可以执行HOT UPDATE,如图所示,将Tuple1更新成Tuple4,此时Tuple1成了死元组,但是要注意的是在清理的时候LP1并不能清除,这个下面会说到。
如图所示,当我们使用HOT UPDATE更新Tuple1时,假如更新后的元组是Tuple4,则Tuple1上的ctid字段会指向LP4,此时的Tuple1也可以认为是死元组,当执行vacuum时能够被清掉,但是LP1仍保留。
三 VACUUM相关参数
我们可以使用select name, min_val, max_val from pg_settings where name ilike '%vacuum%';
来查看所有和VACUUM相关的参数,结果如下:
postgres=# select name, min_val, max_val, boot_val from pg_settings where name ilike '%vacuum%';
name | min_val | max_val | boot_val
---------------------------------------+---------+------------+------------
autovacuum | | | on
autovacuum_analyze_scale_factor | 0 | 100 | 0.1
autovacuum_analyze_threshold | 0 | 2147483647 | 50
autovacuum_freeze_max_age | 100000 | 2000000000 | 200000000
autovacuum_max_workers | 1 | 262143 | 3
autovacuum_multixact_freeze_max_age | 10000 | 2000000000 | 400000000
autovacuum_naptime | 1 | 2147483 | 60
autovacuum_vacuum_cost_delay | -1 | 100 | 2
autovacuum_vacuum_cost_limit | -1 | 10000 | -1
autovacuum_vacuum_insert_scale_factor | 0 | 100 | 0.2
autovacuum_vacuum_insert_threshold | -1 | 2147483647 | 1000
autovacuum_vacuum_scale_factor | 0 | 100 | 0.2
autovacuum_vacuum_threshold | 0 | 2147483647 | 50
autovacuum_work_mem | -1 | 2147483647 | -1
log_autovacuum_min_duration | -1 | 2147483647 | 600000
vacuum_buffer_usage_limit | 0 | 16777216 | 256
vacuum_cost_delay | 0 | 100 | 0
vacuum_cost_limit | 1 | 10000 | 200
vacuum_cost_page_dirty | 0 | 10000 | 20
vacuum_cost_page_hit | 0 | 10000 | 1
vacuum_cost_page_miss | 0 | 10000 | 2
vacuum_failsafe_age | 0 | 2100000000 | 1600000000
vacuum_freeze_min_age | 0 | 1000000000 | 50000000
vacuum_freeze_table_age | 0 | 2000000000 | 150000000
vacuum_multixact_failsafe_age | 0 | 2100000000 | 1600000000
vacuum_multixact_freeze_min_age | 0 | 1000000000 | 5000000
vacuum_multixact_freeze_table_age | 0 | 2000000000 | 150000000
(27 rows)
这里只介绍几个重要参数
- autovacuum:这个是总开关,决定着是否开启AUTO VACUUM,但是需要注意的是,即使设置autovacuum=off 也可能会触发拉起Worker。
- autovacuum_naptime:这个参数控制着Launcher检测拉起Worker的周期,注意的是这个值的真是意图时能够在naptime时间内对所有库都执行一轮清理,因此假设有N个库,那么Launcher每隔naptime/N的时间就会在一个库上拉起一个Worker;
- autovacuum_analyze_scale_factor、autovacuum_vacuum_scale_factor:这两个参数分别控制触发autoanalyze和autovacuum的阈值,分别是0.1和0.2,但这并不是说触发autovacuum的时候一定会触发autoanalyze,实际上他们的触发没有必然联系;
- autovacuum_analyze_threshold 、autovacuum_vacuum_threshold:这两个值是前面两个阈值的基准值,可以设置这两个值来控制小表的触发时机;
四、VACUUM的原理过程
上面简单介绍了死元组的产生场景,接下来介绍VACUUM的原理。
实际严格来说,VACUUM并不是一个进程,而是包括Launcher和Worker两个进程。其中Launcher是常驻进程,负责拉起分配Worker。
下一篇: 阿里云申请免费SSL证书