PostgreSQL-MVCC(多版本并发控制)-实际锁是什么时候获取的



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

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

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

    a( 。一个新的事务开始将名称更新为";bb";(对于id=1(。与此同时,还有另一个事务开始读取相同的数据。

    b( 。创建了一个新的Tuple(postgres中表示一行的不可变对象(,Xmin=当前事务id(假设为200(,Xmax=null以及id=1,name=bb。此外,id=1的旧版本也更新为Xmax=200。读取事务看到Xmin=100的旧版本数据并返回。这种情况下是否需要锁定?我认为没有,但它可能会更新旧元组的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获取独占锁,其他并发更新txn是否等待任何正在进行的txn完成,或者postgres是否有其他处理方式

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

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

使用并发读取进行更新。。。。这种情况下是否需要锁定

当xmax被更新时;重量轻";在保存元组的缓冲区上锁定,一旦字段更新,就会释放该元组(在事务期间不保持(。这个轻量级锁包括一个屏障,以确保任何其他进程都能看到所做的更改,而不是看到过时的缓存版本

读取器要么会看到xmax为0并返回元组,要么会看到xmax为200并看到200尚未提交,并无论如何都返回元组,因为它知道xmax=200表示的锁不适用于它,只是一个读取器。

使用并发更新进行更新。。。

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

最新更新