PostgreSQL中的MVCC 事务隔离

本文探讨了PostgreSQL中的事务隔离和多版本并发控制(MVCC),解释了事务隔离的重要性及其防止数据异常的角色。文章介绍了SQL标准中的四种事务隔离级别,包括脏读、不可重复读和幻读等异常,并分析了PostgreSQL如何实现这些隔离级别,特别是其Read Committed和Repeatable Read级别。此外,还讨论了Serializable级别的特性以及在PostgreSQL中的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作者:Erop Porob
翻译:墨香溪
文章来自Postgres Professional

在本文中,我将开始有关PostgreSQL内部结构的一系列课程(或者说系列集?—总的来说,这个想法很宏大)。

本文中的材料将基于Pavel pluzanov和我正在创建的有关PostgreSQL数据库管理的培训课程(俄语)。并非每个人都喜欢看视频(比如我)或者看幻灯片,即使有注释,也不是很理想。

可惜的是,目前唯一可用的英语课程是2天入门 PostgreSQL 11。

当然,文章与课程内容将不会完全相同。我将只讨论PostgreSQL的基本原理与构成,而忽略数据库管理的内容,但是我将尝试更详细,更深层次的阐述PostgreSQL 的原理。我相信这样的知识对应用程序开发人员和管理员都是有用的。

本文将针对那些已经拥有使用PostgreSQL经验并且至少大致了解PosgresSQL 是什么的人。因为对于初学者来说,本系列会太难了(比较难)。例如,我不会讨论任何有关如何安装PostgreSQL和运行psql的。

所涉及的内容因版本而异,但是我将使用当前的 PostgreSQL 11 的最初版本。

第一个系列的文章将阐述事务隔离和多版本并发控制(MVCC)相关的问题,本系列的计划如下:

1.理解在标准和PostgreSQL 中的事务隔离的实现(本文)。
2.派生文件,文件,页 – 在物理文件级别的变化
3.行的版本,虚拟事务和子事务
4.数据快照和行版本的可见性;事件范围。
5.页内vacuum和HOT更新
6.正常 Vacuum
7.AutoVacuum
8.事务ID 回卷以及 事务冻结。

以下正式开始!

在开始之前,我要感谢Elena Indrupskaya将文章翻译成英文。
注:此系列文章由英文译为中文。

什么是事务隔离,为何如此重要?

可能每个人多多少少都知道事务的存在,知道ACID的缩写,并且听说过隔离级别。但是我们仍然会碰到这样一种观点:事务隔离只与理论有关,在实践中是不必要的。因此,我将花一些篇幅来解释为什么这确实很重要。
我想你应该不会乐于看到应用程序从数据库中获取了不正确的数据,或者应用程序将错误的数据写入了数据库。
但是什么是“正确”的数据?众所周知,可以在数据库级别创建完整性约束,例如NOT NULL或UNIQUE。如果数据始终需要满足完整性约束(之所以如此,是因为DBMS(即数据库管理系统)可以确保),则它们是不可或缺的。

正确和完整是可以等同的吗?不完全是。并非所有约束都可以在数据库级别上指定。有些约束过于复杂,例如,一次操作中含有多个表。即使通常可以在数据库中定义约束,但由于某种原因,它没有定义,但这并不意味着可以违反约束。
因此,正确性要比完整性强,但是我们并不确切的知道这意味着什么。我们只不过承认正确性的“黄金标准”是一个应用程序,如我们希望的那样,是正确编写的,绝不会出错。在任何情况下,如果应用程序没有违反完整性,而是违反了正确性,则DBMS不会记录到这一情况,也不会当场捕获该错误。
在后面,我们将使用术语一致性来指代正确性。
但是,让我们假设应用程序仅按照正确的顺序执行操作。那如果应用程序正确无误,DBMS的作用是什么?
首先,事实证明,正确的操作顺序会暂时破坏数据一致性,这听起来很奇怪,也很合理。一个简单明了的例子是将资金从一个帐户转移到另一个帐户。一致性规则可能像这样:转账永远不会更改帐户上的总金额(此规则在SQL中很难指定为完整性约束,因此它存在于应用程序级别,并且对DBMS不可见)。转帐包括两个操作:第一个操作减少一个帐户上的资金,第二个操作-增加另一个帐户上的资金。第一个操作破坏数据一致性,而第二个操作恢复数据一致性。

一个好的实践是在完整性约束级别上实现上述规则。

如果执行第一个操作后第二个操作不执行该怎么办? 比如:在第二次操作过程中,可能会发生电力故障,服务器崩溃,除于0的情况-- 无论如何,很明显,一致性将被破坏,并且这是不允许的。通常,可以在应用程序级别解决此类问题,但需要付出大量努力;但幸运的是,我们大可不必这样做:这可以由DBMS完成。为此,DBMS必须知道这两个操作是不可分割的整体。也就是,事务。
事实证明很有趣: 因为DBMS知道操作是在一个事务里面,所以它通过确保事务的原子性来帮助保持一致性,而这样做却不用了解特定的一致性规则。
但是还有第二点,更微妙的一点。一旦几个同时发生的事务(分别绝对正确)出现在系统中,它们可能无法一起正常工作。这是因为操作顺序混合了:您不能假定一个事务的所有操作都先执行,然后又执行另一个事务的所有操作。
关于并发:实际上,事务可以在具有多核处理器,磁盘阵列等的系统上并发执行。但是,在分时共享模式下,按顺序执行命令的服务器也一样适用这个逻辑:在某个时钟周期内执行一个事务,在下个时钟周期,执行另外一个事务。有时,这种情况就被术语称作“并发执行”。
正确的事务无法正常工作的情况称为并发执行异常。
举一个简单的例子:如果一个应用程序想要从数据库中获取正确的数据,它至少不能看到其他未提交事务的变化。否则,您不仅可以获取不一致的数据,还可以查看数据库中从未存在过的内容(如果另外一个未提交的事务取消了事务)。这种异常称为脏读。
还有其他更复杂的异常,我们将在稍后讲到。
当然,避免并发执行是不可能的:否则,我们还谈什么性能? 但是您不能使用不正确的数据。
DBMS再次用来解决这类问题。让你可以使事务像顺序执行一样,好像一个接一个地执行。换句话说-彼此隔离。实际上,DBMS可以混合执行各种操作,但要确保并发执行的结果与某些可能的顺序执行的结果相同。这样可以消除任何可能的异常情况。因此,我们得出了以下定义:

事务是由应用程序执行的一组操作,这些操作将数据库从一个正确的状态转移到另一个正确的状态(一致性),倘若事务已完成(原子性)并且不受其他事务的干扰(隔离)。

此定义将首字母缩写ACID的前三个字母组合在一起。他们紧紧的结合在一起,以至于另外一个字母都变得没有意义。实际上,和字母D(耐久性)也难以分离。确实,当系统崩溃时,它仍具有未提交事务的更改,您需要使用这些更改来恢复数据一致性。
一切都会好起来的,但是实现完全隔离是一项技术难题,会降低系统吞吐量。因此,实际上,经常(不是总是,但几乎总是)使用弱隔离,这可以防止某些但不是全部异常。这意味着确保数据正确性的一部分工作落在应用程序上。因此,了解系统中使用的隔离级别,提供哪些保证,不提供什么以及在这种情况下如何编写正确的代码非常重要。

SQL标准中的隔离级别和异常

SQL标准很早就描述了四个隔离级别。通过列出在此级别同时执行事务时允许或不允许的异常来定义这些级别。因此,要谈论这些级别,有必要了解异常。

我强调,在这一部分中,我们谈论的是标准,也就是说,关于一种理论,实践基于该理论。但与此同时,实践却大相径庭。因此,以下示例都是基于推测,推测银行会对银行账户进行相同的操作。这是很好的例子。当然,尽管这和现实中银行的实际操作无关。

丢失更新

让我们从丢失更新开始。当两个事务读取表的同一行,然后一个事务更新该行,然后第二个事务也更新同一行而不考虑第一个事务所做的更改时,就会发生此异常。
例如,两次事务将使同一帐户上的金额增加100元(原文中使用卢布,翻译中使用人民币)第一个事务读取当前值 1000 ,然后第二个事务读取相同的值。第一笔事务增加金额(更新为 1100)并写入该值。第二个事务的行为方式相同:更新为1100并写入此值。结果,客户损失了100元。SQL标准不允许在任何隔离级别丢失更新。

脏读和读取未提交

脏读我们已经熟悉了。当一个事务读取另一个事务尚未提交的更改时,就会发生此异常。例如,第一笔事务将客户的帐户下的所有资金转移到另一个

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值