postgresql垃圾回收

简介:

之前学习pg vacuum的时候,只是简单的知道pg的mvcc会产生多版本,多版本会有垃圾,垃圾需要处理,vacuum就可以清除这些垃圾。可是一直没有仔细想过到底哪些垃圾是可以清除的,哪些是不能处理的。下面我们仔细研究下这个问题,vacuum可以降低表的年龄可以释放空闲空间,也可以垃圾回收,我们先只单独讨论垃圾回收这一过程。
首先什么样的数据是垃圾数据:
postgres=# create table a(id int);
CREATE TABLE
postgres=# insert into a values(1);
INSERT 0 1
postgres=# select *,ctid from a;

 id ctid
  1 (0,1)

(1 row)
创建好测试表插入第一条数据,这个数据是在0号页面第一行上。
下面更新下这条数据
postgres=# update a set id=10;
UPDATE 1
postgres=# select *,ctid from a;

 id ctid
 10 (0,2)

(1 row)
发现,数据记录在0号页面的第二行上了,原来第一行的记录被更新了将更新的数据放到了第二行上,所以之前的第一条记录就已经没用了,是一条垃圾记录了。但是这时候这条记录并没有被回收,只是隐藏了,下面看下回收的过程:
postgres=# vacuum verbose a;
INFO: vacuuming "public.a"
INFO: "a": found 1 removable, 1 nonremovable row versions in 1 out of 1 pages
DETAIL: 0 dead row versions cannot be removed yet.
There were 0 unused item pointers.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM
可以看到有一条记录被移除了。那是不是有垃圾无法被vacuum回收的情况呢?是有,下面分两种不同版本的来分析下,pg9.4和pg9.6因为pg9.6对快照过旧做了一些改动。先说9.4吧。
9.4:
先模拟不能回收的情况:
会话A:
postgres=# begin;
BEGIN
postgres=# select txid_current();

txid_current

 98978845

(1 row)

postgres=# select pg_backend_pid();

pg_backend_pid

      31156

(1 row)

postgres=#

会话A开始一个事务,申请一个事务号,产看下会话A的pid是多少,不要提交。
会话B:
postgres=# select backend_xid,backend_xmin from pg_stat_activity where pid=31156;

 backend_xid backend_xmin
    98978850

(1 row)

postgres=# select txid_current();

 txid_current

     98978851
(1 row)

postgres=# update a set id =1000;
UPDATE 1
postgres=# select *,ctid from a;

  id ctid
 1000 (0,2)

(1 row)

postgres=# vacuum verbose a;
INFO: vacuuming "public.a"
INFO: "a": found 0 removable, 2 nonremovable row versions in 1 out of 1 pages
DETAIL: 1 dead row versions cannot be removed yet.
There were 1 unused item pointers.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM
postgres=#
会话B先看下会话A的backend_pid和backend_xmin是否有值,然后看下当前最新事务号,更新下a表后进行vacuum,发现有一条垃圾记录无法回收。这个是为什么呢?先说下backend_pid和backend_xmin,backend_pid是当开启一个事务时,申请到事务号是就会有值(是指begin后又插入或者更新操作等),backend_xmin在read commit级别下是每条语句都会有快照,但是语句执行完马上就释放,长时间执行语句是可以看到该值:
会话A:
postgres=# select pg_backend_pid();

 pg_backend_pid

          11194
(1 row)

postgres=# select pg_sleep(20);
会话B:
postgres=# select backend_xid,backend_xmin from pg_stat_activity where pid=11194;

 backend_xid backend_xmin
              98979813

(1 row)
当repeatable read和串行化的隔离级别中,开启一个事务,任意一条语句都会申请到backend_xmin。
呢垃圾回收和这两个值有什么关系呢?9.6以前是只能回收tuple的xmax小于min(backend_xid,backend_xmin)的 ,所以当你有一个长事务时长时间不提交,它之后更新删除的xmax都会大于呢个长事务的backend_xid所以这些垃圾数据就都无法回收,造成表膨胀。当你备份时事务的隔离级别会是repeatable read,时间过长和上面一样会产生表膨胀。
下面说下9.6的变化吧,9.6新增了old_snapshot_theshold,这个是表示当语句持有backend_xmin的时间超过old_snapshot_theshold设定的时间并且读取到的数据块的lsn大于backend_xmin快照存储的lsn时就会发生snapshot too old的情况。下面模拟下这种情况。
会话A:
postgres=# begin transaction isolation level repeatable read;
BEGIN
postgres=# select 1;

 ?column?

        1
(1 row)

postgres=# select *,ctid from a where ctid::text like '%(1427,%' limit 10;

   id ctid
 4735747 (1427,1)
 1209750 (1427,2)
 3293719 (1427,3)
  555445 (1427,4)
 3724432 (1427,5)
 3114037 (1427,6)
  182685 (1427,7)
  213731 (1427,8)
 4052679 (1427,9)
 1677525 (1427,10)

(10 rows)

postgres=# select *,ctid from a where ctid::text like '%(1427,%' limit 10;
ERROR: snapshot too old
STATEMENT: select *,ctid from a where ctid::text like '%(1427,%' limit 10;
ERROR: snapshot too old
会话B:
postgres=# select backend_xid,backend_xmin from pg_stat_activity where pid=30504;

 backend_xid backend_xmin
              109690248

(1 row)

postgres=# update a set id =1090 where id =4735747;
UPDATE 1
postgres=#
会话A先开启repeatable read隔离级别得到一个backend_xmin,扫描1427号页面,B会话更新1427号页面任意一行记录,等到一个old_snapshot_theshold的时间,再去A会话里查找1427号页面就会发现snapshot too old这个错。
那这个对垃圾回收有哪些影响呢?之前是看tuple的xmax是否大于min(backend_xid,backend_xmin),大于就无法回收,现在是tuple的xmax是否大于
max(old_snapshot_theshold中记录的最老记录,backend_xid,backend_xmin),
old_snapshot_theshold是每分钟记录最大的backend_xid没有取当前最小事务号.
下面模拟9.6新增可以回收的情况:
会话A:
postgres=# begin;
BEGIN
postgres=# select pg_sleep(200);
会话B:
postgres=# insert into a values(1);
INSERT 0 1
postgres=# insert into a values(1);
INSERT 0 1
postgres=# delete from a where id=1;
DELETE 2
postgres=# select * from a limit 1;

   id

 3137507
(1 row)

postgres=# vacuum verbose a;
INFO: vacuuming "public.a"
INFO: "a": found 0 removable, 452 nonremovable row versions in 2 out of 1428 pages
DETAIL: 2 dead row versions cannot be removed yet.
There were 2 unused item pointers.
Skipped 0 pages due to buffer pins.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM
postgres=# vacuum verbose a;
INFO: vacuuming "public.a"
INFO: "a": removed 2 row versions in 2 pages
INFO: "a": found 2 removable, 450 nonremovable row versions in 2 out of 1428 pages
DETAIL: 0 dead row versions cannot be removed yet.
There were 2 unused item pointers.
Skipped 0 pages due to buffer pins.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM
postgres=#
完整过程是先,开启A会话,A会话得到pg_backend_xmin,B会话插入记录并删除,进行第一次vacuum发现呢条记录无法回收,等A运行一段时间old_snapshot_theshold获取到的backend_xid大于backend_xmin就可以回收这些垃圾记录了, 需要先查一下这个表才能回收(不知道什么原因)。

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
存储 算法 关系型数据库
PostgreSQL 垃圾回收参数优化之 - maintenance_work_mem , autovacuum_work_mem
PostgreSQL 垃圾回收参数优化之 - maintenance_work_mem , autovacuum_work_mem
4113 1
|
存储 弹性计算 算法
PostgreSQL 垃圾回收参数优化之 - maintenance_work_mem , autovacuum_work_mem
标签 PostgreSQL , 垃圾回收 , 索引扫描 , 内存 背景 夜谈PostgreSQL 垃圾回收参数优化之 - maintenance_work_mem , autovacuum_work_mem。 http://www.postgres.cn/v2/news/viewone/1/398 https://rhaas.blogspot.com/2019/01/how-much
2909 0
|
关系型数据库 Java 数据库
PostgreSQL 老湿机图解平安科技遇到的垃圾回收"坑"
背景 从海安那里反馈的一个问题,是平安科技在使用PostgreSQL的过程中,遇到的一个有些"不可思议"的问题。 一张经常被更新的表,通过主键查询这张表的记录时,发现需要扫描异常多的数据块。 其实原因有2。 .1. 长事务有关,我在很多文章都提到过,PG在垃圾回收时,只判断垃圾版
7345 0
|
Java 关系型数据库 PostgreSQL
PostgreSQL物理"备库"的哪些操作或配置,可能影响"主库"的性能、垃圾回收、IO波动
标签 PostgreSQL , 物理复制 , 垃圾回收 , vacuum_defer_cleanup_age , hot_standby_feedback , max_standby_archive_delay , max_standby_streaming_delay 背景 PostgreSQL 物理备库的哪些配置,或者哪些操作,可能影响到主库呢? 首先,简单介绍一下PostgreSQL的物理备库,物理备库就是基于PostgreSQL WAL流式复制,实时恢复的备库。
4873 0
|
存储 算法 Java
G1原理—5.G1垃圾回收过程之Mixed GC
本文介绍了G1的Mixed GC垃圾回收过程,包括并发标记算法详解、三色标记法如何解决错标漏标问题、SATB如何解决错标漏标问题、Mixed GC的过程、选择CollectSet的算法
G1原理—5.G1垃圾回收过程之Mixed GC
|
存储 算法 Java
G1原理—6.G1垃圾回收过程之Full GC
本文详细探讨了G1垃圾回收器对Full GC(FGC)的优化处理,涵盖FGC的前置处理、整体流程及并行化改进。重点分析了传统FGC串行化的局限性以及G1通过Region分区和RSet机制实现并行标记的优势,包括任务窃取提升效率、跨分区压缩以生成空闲Region等技术细节。此外,文章还介绍了G1的新特性——字符串去重优化,通过判断char数组一致性减少重复字符串占用内存,从而提升内存使用效率。总结部分全面回顾了G1在FGC中的各项优化措施及其带来的性能改善。
G1原理—6.G1垃圾回收过程之Full GC
|
存储 算法 Java
G1原理—4.G1垃圾回收的过程之Young GC
本文详细解析了G1垃圾回收器中YGC(Young Generation Collection)的完整流程,包括并行与串行处理阶段。内容涵盖YGC相关参数设置、YGC与Mixed GC及FGC的关系、新生代垃圾回收的具体步骤(如标记存活对象、复制到Survivor区、动态调整Region数量等),以及并行阶段的多线程操作和串行阶段的关键任务(如处理软引用、整理卡表、重构RSet)。
G1原理—4.G1垃圾回收的过程之Young GC
|
算法 网络协议 Java
【JVM】——GC垃圾回收机制(图解通俗易懂)
GC垃圾回收,标识出垃圾(计数机制、可达性分析)内存释放机制(标记清除、复制算法、标记整理、分代回收)
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
1351 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS

推荐镜像

更多