postgresql垃圾回收

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介:

之前学习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
3516 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
2632 0
|
关系型数据库 Java 数据库
PostgreSQL 老湿机图解平安科技遇到的垃圾回收"坑"
背景 从海安那里反馈的一个问题,是平安科技在使用PostgreSQL的过程中,遇到的一个有些"不可思议"的问题。 一张经常被更新的表,通过主键查询这张表的记录时,发现需要扫描异常多的数据块。 其实原因有2。 .1. 长事务有关,我在很多文章都提到过,PG在垃圾回收时,只判断垃圾版
7051 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流式复制,实时恢复的备库。
4459 0
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
73 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
3月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
2月前
|
存储 Java PHP
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
73 0
|
3月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制(GC)
本文将探讨Java的自动内存管理核心——垃圾回收机制。通过详细解析标记-清除算法、复制算法和标记-整理算法等常用垃圾回收算法,以及CMS、G1等常见垃圾回收器,帮助读者更好地理解Java应用的性能优化和内存管理。同时,探讨分代收集、分区收集等策略在实际项目中的应用。结语部分总结了垃圾回收机制在Java开发中的重要性,并展望了未来可能的发展。
74 0
|
4月前
|
缓存 监控 Java
"Java垃圾回收太耗时?阿里HBase GC优化秘籍大公开,让你的应用性能飙升90%!"
【8月更文挑战第17天】阿里巴巴在HBase实践中成功将Java垃圾回收(GC)时间降低90%。通过选用G1垃圾回收器、精细调整JVM参数(如设置堆大小、目标停顿时间等)、优化代码减少内存分配(如使用对象池和缓存),并利用监控工具分析GC行为,有效缓解了高并发大数据场景下的性能瓶颈,极大提升了系统运行效率。
97 4