2

根据我的理解,postgres 使用两个额外的字段 Xmin 和 Xmax 来实现 mvcc,假设我们有 Employee 表,其中包含 id 和 name 列。

下面是一些 crud 操作以及它们如何同时工作(考虑隔离级别 = READ_COMMITTED),问题是何时何地获取实际锁。

  1. 插入-> 新事务插入一条新记录,该记录在提交之前对其他事务不可见,因此在这种情况下没有问题,也不需要锁定或版本控制。假设 id = 1,name = "aa" 被插入。Postgres 为 mvcc Xmin = current txn id(假设为 100)和 Xmax = 0/null 添加了 2 个额外的列。
id  |   name | Xmin | Xmax
------------------------------
1   |   aa   | 100  | null
  1. 使用并发读取更新-

    一个)。一个新事务开始将名称更新为“bb”(对于 id = 1)。同时还有另一个事务开始读取相同的数据。

    乙)。使用 Xmin = 当前事务 id(假设为 200)和 Xmax = null 以及 id = 1、name = bb 创建一个新的元组(postgres 中的不可变对象表示一行)。旧版本的 id = 1 也被更新为 Xmax = 200。读取事务看到旧版本的数据 Xmin = 100 并返回。 在这种情况下是否需要任何锁定?我认为没有,但它可能会更新旧元组的 Xmax。

以下是多个版本的相同记录(仅用于解释目的),最新版本的 Xmax = null。

id  |   name | Xmin | Xmax
------------------------------
1   |   aa   | 100  | 200
1   |   bb   | 200  | null
  1. 使用并发更新进行更新-

    一个)。交易(txn id = 300)开始将 id = 1 更新为 name = cc。另一个事务(txn id = 400)开始将同一记录(id = 1)更新为name = dd。如果这种情况也通过创建新元组并标记旧元组的 Xmax 以同样的方式进行,那么我认为它会产生问题,因为 300 和 400 都会创建一个新元组并标记旧元组的 Xmax = txn id。在这种情况下,更新可能会丢失。

在这种情况下,排他锁是由第一个 txn 获取的,其他并发更新 txns 会等到任何正在进行的 txn 完成,或者 postgres 有其他方式处理它吗?

4

1 回答 1

3

插入 -> 新事务插入一条新记录,该记录在提交之前对其他事务不可见,因此在这种情况下没有问题,也不需要锁定或版本控制。

这不是真的。插入的元组在插入时被锁定。例如,如果存在唯一约束并且其他人试图插入冲突的元组,这很重要。

使用并发读取更新......在这种情况下是否需要任何锁定

当 xmax 被更新时,在保存元组的缓冲区上有一个“轻量级”锁,它将在字段更新后立即释放(在事务期间不保持)。这种轻量级锁包含一个屏障,以确保任何其他进程都能看到所做的更改,而不是看到它的陈旧缓存版本。

读者要么会看到,取决于它何时到达那里,要么 xmax 为 0 并返回元组,要么它会看到 xmax 为 200 并看到 200 尚未提交,并返回元组,因为它知道锁由 xmax = 200 表示的不适用于它,只是一个读者。

使用并发更新更新...

第一个将其 id 写入将要过时的元组的 xmax 的进程将获胜。第二个将在 xmax 中看到其他人的有效 id,并阻塞直到其他事务提交或回滚,然后决定要做什么。由于保存元组的缓冲区上的轻量级锁,它们不能同时更新 xmax 而不会注意到彼此的变化。

于 2020-07-29T16:40:22.167 回答