MVCC and VACUUM

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: 这是Robert Haas写的一片关于mvcc,vacuum的文章,我觉得写的比较透彻而且通俗易懂,从一个比较高的角度阐述了什么是mvcc,为什么我们需要mvcc。

MVCC and VACUUM

有经验的PostgreSQL用户和开发者经常滔滔不绝的说MVCC和VACUUM者两个专业的术语,好像每个人都应该知道它们是什么以及如何工作,但是实际上真正理解这些人的人并不多。这篇博文我将从我的角度解释MVCC是什么以及为什么PostgreSQL使用它,VACUUM是什么以及它是如何工作的,以及为什么我们需要VACUUM来实现MVCC。 此外,我还会为那些想要了解更多信息的人提供一些有用的链接供进一步阅读。


PostgreSQL是一个关系数据库,因此提供对事务的支持,即ACID:原子性,一致性,隔离性和持久性。让我们暂时忽略一致性和持久性。原子性意味着交易是“全有或全无”,要么事务提交则其所有更改生效,或者中止则所有更改都不会生效。隔离意味着每个事务都应该不知道还有其他事务同时执行。例如,如果事务A更新a=1的两行,使a=2,与此同时事务B删除a = 2的所有行。正常应该是如果 A“先发生”,更新完成之后两行都被B删除,或者B“先发生”,因此两行都不会被删除,而不应该出现A更新之后其中一行被删除,另一行因为B删除时还未更新因此保留下来的情况。总而言之,这意味着数据库希望给用户一种印象,即在他们的事务正在进行时不会发生并发更改,并且当他们的事务提交时,它所做的所有更改都会立即变为每个人同时可见。


不仅仅是PostgreSQL,一般的关系型数据库基本上使用两种方法来解决这个问题。 第一种是两阶段锁定:事务对其读取的每一位数据采用共享锁,并对其写入的每一位数据进行独占锁定。 提交时,会释放这些锁; 如果它中止,它会回滚它所做的更改然后释放锁定。 由于任何已修改的数据都受独占锁保护,因此在提交之前,并发事务不会看到该更改。 同时,读取操作可以共存,因为他们的锁都是共享的。 不幸的是,当读取和写入数据时,这种方法的并发性很差:读取操作和写入操作相互阻塞,因为读取共享的锁与写入所采用的排他锁相冲突。 锁对于并发性是不利的,并且两阶段锁需要在事务的整个持续时间内保持独占锁(以及共享锁,以获得完全正确性)。


提供原子性和隔离事务的第二种方法是多版本并发控制(MVCC)。基本思路很简单:不要锁定我们想要更新的行,而是创建它的新版本,最初只对创建它的事务可见。一旦更新事务提交,我们将使新行对在该点之后开始的所有新事务可见,而现有事务继续查看旧行。这样,每个事务实质上看到是数据库的一致快照。最后,当没有剩余的事务可以看到旧版本的行时,我们可以丢弃它。这种方法比两阶段锁具有更好的并发性,这就是许多现代数据库系统使用它的原因。但是,我们已经将并发问题转化为空间管理问题。通过两阶段锁定,可以在原地更新行,因为更新事务持有独占锁,在释放锁之前,没有其他人可以看到修改。对于MVCC,这将无法工作,因为我们需要至少在短时间内保留这两个版本(或者如果有长时间运行的并发事务,可能需要很长时间)。旧行版本必须保存在其他位置,新版本放在其位置,或旧行版本必须保持不变,新版本存储在其他位置。不同的数据库采用不同的处理方法


PostgreSQL解决这个问题的方法是存储UPDATE创建的新行版本,基本上与存储INSERT生成的全新行的方式相同。一旦UPDATE提交,未来的SQL语句(或未来的事务,取决于您选择的隔离级别)将看到新的行版本,而旧的版本只对已经运行的语句(或事务)可见。最终,将不再有任何可以看到旧行版本的运行,此时它们已经dead。类似地,当DELETE提交时,最终将没有任何运行可以看到旧行版本,此时它们同样dead。类似地,如果INSERT中止,它插入的新行版本从仅对插入事务可见到完全对任何人不可见,因此也是dead,同样当UPDATE中止时,任何它创造的新版本都是dead。这也是为什采用这种方式的数据库的回滚是瞬间完成的。 Heroku开发中心有一篇写得很好的文章,解释了数据库内部原理的一些细节.

这使我们需要VACUUM。任何使用MVCC实现事务隔离的系统都必须有一些方法来清理过期数据。如果没有,过期版本的数量将无限制地增长,数据库大小将无限制地增长,这是不能接受的。在某些时候,我们必须摆脱过期版本,以便可以重用空间。在PostgreSQL中,这是VACUUM的工作。其他一些系统对不同类型的过期版本使用不同的清理机制。例如,由中止事务创建的过期版本可能与由已提交事务创建的死行版本不同地处理。在PostgreSQL中,我们以相同的方式处理这些问题。值得一提的是,在某些情况下,可以使用称为HOT修剪的一次性页面机制清理死行版本(有关技术细节,请参阅README.HOT)。这些处理方式实际上很常见;然而,HOT修剪是一种“尽力而为”的策略,VACUUM是确保工作完成并完成HOT修剪无法完成的任何清理的后盾。(这个简短的解释省略了许多有趣的细节,但这篇文章已经变得很长了。)


现在我们来讲讲VACUUM的工作原理。首先,它扫描表中所有可能包含过期数据的页面。一种被称为visibility map的数据结构记录了自上一个VACUUM以来哪些页面已被修改,没有记录在里面的页面会被跳过。就这样,它会从这些页面中删除过期版本,并使这些元组占用的空间可供重用。在此过程中,过期版本被替换为一种类似墓碑的记号 - 在技术上,指向过期数据行的指针会被标记为LP_DEAD。另外,它会扫描表中的每个索引,并删除指向第一阶段中标识的LP_DEAD指针的所有索引条目。一旦完成此阶段,它将返回到表并删除过期标志 - 从技术角度来说,LP_DEAD行指针标记为LP_UNUSED。完成后,这些行指针可以重用于新元组。如果在删除索引条目之前重用这些行指针,我们可能最终会得到与它们指向的行版本不匹配的索引条目,这可能导致查询返回错误。


上一段中使用术语“line pointer”,但没有定义它。 每个页面都以一系列行指针开头,这些行指针标识页面上每个元组的起始位置以及该元组的长度。 有一些特殊类型的行指针,包括LP_UNUSED(这意味着数组槽可用于新元组),LP_DEAD(如上所述,这意味着元组已从页面中删除但是行指针槽还不能重用)和LP_REDIRECT(与HOT有关,但超出了本文章的范围)。


简而言之,VACUUM扫描自上一个VACUUM以来已更改的表的部分,然后完整地扫描索引,然后再次扫描表的更改部分。 通过这样做,它确保删除过期版本和引用它们的索引条目,并确保引用这些过期版本的行指针可供重用。


最后的一些说明:

1.普通的VACUUM与VACUUM FULL非常不同,后者实际上重建了整张表

2.除了删除过期元组的功能外,VACUUM还可以防止事务ID重复,更新统计信息,以及更新visibility map。 更新visibility map非常重要,不仅可以提高未来的VACUUM操作效率,还可以使index-only扫描更好地工作。 这些都包含在文档中。

3.通常,您不需要手动运行VACUUM,因为autovacuum守护程序将安排自动运行它。 但是,有时候手动运行VACUUM很有用。 这可能是一个好主意,例如,在大量装载之后,或在一天业务低峰是运行,减少在高峰时区运行vacuum。


博客地址
原文地址

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
存储 安全 索引
vacuum freeze无法回收事务号问题分析
vacuum freeze报错问题分析
4490 0
|
24天前
MVCC是什么
MVCC是多版本并发控制,为每次事务生成一个新版本数据,每个事务都有自己的版本,从而不加锁就拒绝读写冲突,这种读叫做快照读。只在读已提交和可重复读中生效。 实现原理由四个东西保证,他们是: undolog日志:记录了数据历史版本 readView:事务进行快照读时产生的视图,记录了当前系统中活跃的事务id,控制哪个历史版本对当前事务可见 隐藏字段DB_TRC_ID: 最近修改记录的事务ID 隐藏字段DB_Roll_PTR: 回滚指针,配合undolog指向数据的上一个版本
|
4月前
|
存储 Java 数据库
|
3月前
|
关系型数据库 MySQL 数据库
InnoDB 的 MVCC 实现原理
InnoDB 的 MVCC 实现原理
49 0
|
4月前
|
关系型数据库 MySQL 数据库
为什么需要MVCC 隔离级别
【8月更文挑战第5天】
55 7
|
6月前
|
关系型数据库 MySQL 数据库
InnoDB-MVCC多版本控制详解
InnoDB-MVCC多版本控制详解
|
存储 算法 Oracle
PostgreSQL的MVCC vs InnoDB的MVCC
PostgreSQL的MVCC vs InnoDB的MVCC
99 0
PostgreSQL的MVCC vs InnoDB的MVCC
|
存储 关系型数据库 Java
MVCC你了解多少?
MVCC(Multi-Version Concurrency Control)是一种并发控制机制,用于解决数据库中并发访问数据时可能出现的读-写冲突问题。MVCC通过为每个事务分配一个唯一的事务ID,并为每个数据项维护多个版本,使得读操作可以同时进行,从而提高并发性能。 MVCC的核心思想是通过版本号或时间戳来区分不同的事务和数据版本。当一个事务开始时,它会被分配一个唯一的事务ID,并在执行读操作时,只能看到在该事务开始之前已经提交的数据版本。这样,即使其他事务正在修改数据,当前事务也不会受到影响,从而避免了读-写冲突。
151 0
|
固态存储 关系型数据库 开发者
PG13并行vacuum
PG13并行vacuum
161 0
|
关系型数据库 MySQL 数据库
简单聊聊MVCC
本文章仅仅是从一个点来讲MVCC,比较粗浅,并不能代表这就是全部的MVCC。网上还有许多其他详细的MVCC介绍文章,可以结合起来阅读。
简单聊聊MVCC