PostgreSQL - MVCC(多版本并发控制) - 何时获取实际锁?

PostgreSQL - MVCC (multi-version concurrency control) - When is actual lock acquired?

据我了解,postgres使用了两个额外的字段Xmin和Xmax来实现mvcc, 假设我们有 Employee table 和 id 和 name 列。

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

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

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

    b).一个新的元组(代表一行的 postgres 中的 immutable 对象)被创建,Xmin = 当前事务 id(比方说 200)和 Xmax = null 以及 id = 1,name = bb。 id = 1 的旧版本也更新为 Xmax = 200。读取事务看到旧版本的数据 Xmin = 100 和 returns。 在这种情况下是否需要任何锁定?我认为不,但它可能会更新旧元组的 Xmax。

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

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

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

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

Insert -> A new transaction inserts a new record which is not visible to other transactions until it is committed, so no issues in this case and no lock or version control is required.

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

Update with concurrent Read....Is there any locking required in this case

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

reader 要么看到 xmax 为 0 而 return 元组,要么看到 xmax 为 200 而 200 不是尚未提交,并且 return 元组仍然存在,因为它知道 xmax = 200 表示的锁不适用于它,仅仅是 reader.

Update with concurrent Update...

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