Postgres,MVCC和锁定


8

我有一系列类似于以下内容的SQL语句:

BEGIN;
SELECT counter FROM table WHERE id=X FOR UPDATE;
REALLY COMPLEX QUERY;
UPDATE table SET counter=Y WHERE id=X;
END;

我想防止在重新计算计数器值时读取该计数器,但是根据Postgres的文档,“行级锁不影响数据查询;它们仅阻止写入同一行的写入器。”

问题:

  1. 如果不阻止读取,那么“排他性”行锁又有什么意义呢?只是为了防止其他事务获取共享锁吗?
  2. 如果我使用SELECT ... FOR SHARE读取该行,是否会达到与“排他”锁相同的效果?
  3. 是否可以为表/模式/数据库关闭MVCC并允许就地写入?

Answers:


5

到1)只要您的交易未提交,其他任何会话都将读取您的交易修改过的数据,就像在“ BEGIN”语句之前一样。事务提交后,它将立即读取计数器的新值。关键是其他人不必等待,并且总会看到一致的数据库。

到2),3)为什么不尝试使用“ ACCESS EXCLUSIVE”?(请参阅http://www.postgresql.org/docs/current/static/explicit-locking.html

编辑:如果您不喜欢使用“ ACCESS EXCLUSIVE”锁来锁定整个表,则还可以使用“ Advisory Lock”(请参阅​​上面链接中的13.3.4节)。


1

如果要阻止的读取是同一事务的同时执行,只是具有不同的数据,请使用带有returning子句的UPDATE语句。

查看有关UPDATE语句的文档。为了回答有关排他行锁定点的问题,这是为了防止同时更新导致数据不一致。读者可以随时获得一致的数据库视图。


1

在您提供的代码示例中,对该行的所有读取将看到旧值,直到该事务提交为止。

1)考虑代码示例同时运行两次,并且具有相同的值X。如果实例A已运行,select ... for update则它将锁定该行,直到提交为止。实例B这样做,将阻止尝试以相同方式获取该锁。只有当A提交时,B才能继续。然后,B将在其最终更新中得到值A。

如果Y取决于select ... for update查询读取的是什么值,或者取决于查询在“复杂”部分中的另一个读取值,则其效果与串行运行的效果相同-您不会获得其中一个结果是丢弃。

如果Y纯粹是中间复杂查询的结果,则并行运行它而不使用select ... for update它们都会进行相同的更新。

2)for share将允许该行上的其他选择继续进行,但将导致其他任何尝试select ... for updateupdate ...阻塞的操作,直到完成为止。如果示例代码同时运行两次,它们将在到达该update语句时陷入僵局,从而导致其中之一被中止。

3)否。这样做很危险,因为读者可能会读取半更新的字段。(或者读取开始读取后更新的字段的其余部分。)它也可能破坏一致性,向读取器显示在其事务开始之后更新的值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.