Navicat 博客

PostgreSQL 中的多版本并发控制 2023 年 5 月 12 日,由 Robert Gravelle 撰写

大部分的数据库系统都使用锁定来进行并发控制,而 PostgreSQL 的做法就略有不同。它使用多版本模型(也称为多版本并发控制,Multi-Version Concurrency Control,简称 MVCC)来维持数据的一致性。因此,在查询数据库时,无论基础数据的当前状态如何,每个事务都会看到一段时间前的数据快照。这可以防止事务看到由其他并发事务正在更新同一数据而引起的数据不一致,也为每个数据库会话提供事务隔离。这篇文章将简要概述 MVCC 协议的工作原理,并介绍 MVCC 方法的一些优缺点。

MVCC 协议的解释

锁定模型和 MVCC 之间的主要区别在于后者确保读取数据的事务永远不会阻止写入数据的事务,反之亦然。

在 MVCC 中,每个事务都有一个事务时间戳,指示 (a) 事务何时开始以及 (b) 事务何时更新某个数据项,例如字段、记录或表。它会创建数据项的新版本,同时还保留旧版本。每个版本都提供:

  • 写入时间戳(write-timestamp)表示创建它的事务的时间戳(即事务开始的时间),和
  • 读取时间戳(read-timestamp)表示所有读取它的事务中的最新时间戳。

MVCC 协议的基本概念是事务管理器只允许可以与在其时间戳时刻全部执行的所有事务一致的操作。这被称为假定执行顺序。数据库研究员 Jan Hidders 解释了事务管理器如何做到这一点,如下所示:

如果事务想要读取一个项目,它可以访问它在假定执行顺序中应该读取的版本。这将是在其自己的时间戳之前具有最新写入时间戳的那个版本。例如,如果存在写入时间戳为 5、12、20 的版本,事务的时间戳为 14,那么这个事务按照假定执行顺序会读取写入时间戳为 12 的版本。

如果事务想要写一个项目,当它被允许读取另一个版本时,则会检查是否没有先前允许的读取操作,并且没有读取操作在假定执行顺序中会读取由请求的写入操作引致的新版本。例如,再次假设我们有写入时间戳为 5、10 和 16 的版本。此外,假设这些版本的读取时间戳分别为 8、12 和 20。当时间戳为 11 的事务想要更新项目时,则会出现问题,因为时间戳为 10 的版本被时间戳为 12 的事务读取。因此,如果创建时间戳为 11 的版本,时间戳为 12 的事务在假定执行顺序中不会看到时间戳为 10 的事务创建的版本,而是会看到现在将要使用时间戳 11 创建的版本。另一方面,如果时间戳为 14 的事务想要写入项目,这就没有问题,因为据我们所知,在假定执行顺序中,在 t=12 之后,该项目直到在 t=16 更新时才被其他事务读取。

MVCC 的优点和缺点

优点

  • 从上面的描述中可以看出所有读取操作总是立即被允许。而锁定方法通常不是这种情况,在这种方法中,读锁可能会因为现有的写锁而被拒绝。
  • 与锁定方法相比,它倾向于允许立即竎行更多的写入操作。

缺点

  • 如果写入操作被拒绝,除了回滚或重新启动事务之外别无选择。一旦更新被拒绝,如果我们稍后重试,它也将被拒绝。这与锁定方法不同,后者我们通常可以等到锁定变为可用。 正是出于这个原因,MVCC 被归类为一种乐观协议。如果没有冲突,它非常高效的,但一旦有冲突,很多工作可能前功尽弃。
  • 存储一个项目的多个版本需要更多的存储空间。而锁定方法只需要存储一个版本。
  • 删除不再需要的版本会导致一些额外的工作。

关于 PostgreSQL 中多版本并发控制的结语

总括来说,本文概述了 MVCC 协议的工作原理,并介绍了它的一些优缺点。

有兴趣使用 PostgreSQL 吗?你可以免费试用 Navicat 16 for PostgreSQL 14 天!

Navicat 文章
频道条目
分享
文章归档