PostgreSQL文档 说 :
Read Committed是 PostgreSQL 中的默认隔离级别。当事务使用此隔离级别时,SELECT
查询(不带FOR UPDATE
/SHARE
子句)只能看到在查询开始之前提交的数据;它永远不会看到未提交的数据或并发事务在查询执行期间提交的更改。实际上,SELECT
查询会在查询开始运行的那一刻看到数据库的快照。(……)
我以这样的方式理解它,如果我在没有明确指定隔离级别的情况下进行事务:
SELECT "id" FROM "table" WHERE /* … */
然后我会在应用程序中处理结果,然后:
UPDATE "table" SET "flag" = TRUE WHERE "id" IN (/* … */)
然后我会得到想要的结果,即更新所有具有所需
ID的行,而不管其他哪些事务在那里删除或更改。但是从有这样的事情来看
SELECT /* … */ FOR UPDATE
,不是吗?
什么情况下SELECT /* … */ FOR UPDATE
需要使用?
事务隔离级别对此有何影响?你需要一个
SELECT /* … */ FOR UPDATE
级别
SERIALIZABLE
吗?
只要您的交易严格按照一个接一个的进行,您就很好,没有问题。除了性能。并且为了提高性能 - 有必要允许并行执行事务。并且这里开始了一个丰富而神奇的世界
concurrently control
。此外,不仅在数据库中,而且在至少并行执行某些操作的任何地方。人们通常更了解金钱,所以让我们谈谈金钱。假设有一个用户,他的账户里有 100 钱。用户可以花掉它们,你检查余额
select balance ...
,然后在购买时更新余额update ... set balance = ? where ...
。在一个快乐的日子里,不知怎的,这个用户的两个购买请求同时出现了。一个50块钱,第二个70块钱。其中一个必须被拒绝,因为。钱是不够的。但结果是,两次购买都通过了,你遇到了问题,你卖了一些不必要的东西。这在用户的余额中甚至是不可见的。如何?这是典型的
race condition
,两个事务首先读取数据,然后在本地做一些事情,然后再写一些事情。在对资源的竞争访问中,他们只在最后一步进行战斗,稍后开始更新数据的事务首先等待第一个事务的完成。然后她老生常谈地将平衡改写为她认为正确的平衡。所谓
lost update
异常。也就是说,在目前的形式中,这两个交易被错误地序列化了。为了正确执行逻辑,第二笔传入交易必须等待第一笔交易的结果才能读取用户的余额。但是数据库并没有对此进行警告,很自然地,这第一次选择被认为没有干扰其他人。
这只是为了警告 DBMS 我们计划对数据做一些事情,因此我们需要以不同的方式序列化事务,并且还有
FOR SHARE
一些FOR UPDATE
补充。因此,顺便说一下,它们记录在显式锁定部分。FOR SHARE
读入许多线程而不会相互阻塞。FOR SHARE
但是如果有人要更新这一行,那么他就会站在等待队列中,直到所有持有读锁的事务都完成,同时延迟所有后续FOR SHARE
事务。FOR UPDATE
,那么我们可以确定在我们的事务结束之前没有其他事务能够更新该行。也就是说,如果没有这个,从 DBMS 的角度来看,竞争事务可以在逻辑上序列化,但对于应用程序的业务逻辑来说是错误的。
可序列化
Postgresql 的 Serializable 实现监视将破坏事务隔离的更改,所有受影响的事务将收到序列化错误。应用程序本身必须准备好在此隔离级别上工作,并期望随时收到序列化错误,准备好再次重试事务。
我在这里需要它吗
FOR UPDATE
- 只需引用文档中有关减少可序列化隔离级别的性能下降的提示:此隔离级别不需要显式锁。
将使用设置中指定的级别
default_transaction_isolation
。也就是说,它可以是 Serializable 而不是 Read Committed。您将获得与条件匹配的所有行的更新。但是否需要这个结果是关键问题。