【问题标题】:Transaction in PostgreSqlPostgreSql 中的事务
【发布时间】:2016-03-29 22:08:18
【问题描述】:

我想在 PosgreSql 中通过 java 实现以下场景:

  • 用户选择数据
  • 用户启动事务:插入、更新、删除数据
  • 用户提交事务

我希望在交易期间其他用户无法使用数据。如果当其他用户尝试更新表时我得到一个异常就足够了。

我尝试使用select for updateselect for share,但它也会锁定数据以供读取。我尝试使用lock 命令,但我无法获得锁 (ERROR: could not obtain lock on relation "fppo10") 或其他事务在尝试提交事务时获得锁,而不是在更新数据时。

是否存在一种在事务开始时锁定数据的方法,以防止对updateinsertdelete 语句的任何其他调用?

这个场景在 DB2 数据库上成功运行了几年。现在我需要同样的应用程序来为 PostgreSql 工作。

【问题讨论】:

  • 一方面,您说您希望数据在交易期间不可供他人使用。另一方面,您不喜欢select for update 锁定数据以供阅读,在我看来,这显然是“对其他人不可用”。没有锁会阻止其他客户端尝试执行更新语句;有些种类会让他们等待。你真正想要什么?
  • @Mike Sherrill'Cat Recall' 问题是,我不知道用户是否想在调用 select 语句的那一刻更新数据。任何更新后都清楚。但为时已晚。
  • 无法知道其他用户想要做什么。阅读PostgreSQL transaction isolation levels
  • @Mike Sherrill 'Cat Recall' 我读过。我在 db2 上有工作应用程序。现在我希望这个应用程序在 postgre 上以同样的方式工作。但这似乎是不可能的:(。
  • 根据您的 DB2 版本,如果行已被锁定以进行更新,则游标稳定性(又名已提交读)将返回单元格的 prior 值。也就是说,它的行为就像在更新语句发生之前读取并返回了该行,即使实际情况并非如此。因此,即使是您的 DB2 应用程序也可能没有按照您的预期进行。 IOW,这个想法是“修复您的应用程序以在并发环境中工作”。

标签: java postgresql jdbc transactions db2


【解决方案1】:

尝试“行独占”表锁,而不是 select for update

LOCK TABLE YourTable IN ROW EXCLUSIVE MODE;

根据documentation,这个锁:

UPDATE、DELETE 和 INSERT 命令在 目标表(除了任何其他的 ACCESS SHARE 锁 参考表)。一般来说,这种锁定模式将被任何 修改表中数据的命令。

请注意,锁的名称令人困惑,但它确实锁定了整个表:

请记住,所有这些锁定模式都是表级锁定,即使 名称中包含“行”一词;锁定模式的名称是 历史的

【讨论】:

  • 根据文档它看起来不错,但是当我尝试它时,在更新表的那一刻没有抛出异常,而是在调用提交时
【解决方案2】:

最后,我想我明白了你想要的。
这本身不是一个“事务”问题(根据要处理的表的数量和所需的语句,您甚至可能不需要一个),这是一个应用程序设计问题。你有两种一般的方法来处理这个问题;乐观锁和悲观锁。

悲观锁是显式地获取并持有一个锁。当您可以保证您更改行以及与之相关的内容,并且您的交易将是时,最好使用它。您将在向帐户添加销售时更新“当前余额”等情况下使用它,一旦购买完成(更新将会发生,交易持续时间短,因为此时没有进一步的选择)。如果用户读取一行然后去吃午饭(或度假......),悲观锁定会变得令人沮丧。

乐观锁定是读取一行(或一组),并且采用任何类型的数据库层锁定。如果您只是阅读行,而没有立即更新任何行的计划,则最好使用它。通常,行数据将包含一个“版本”值(递增的计数器或最后更新的时间戳)。如果您的应用程序要更新该行,它会比较数据的原始值以确保它没有被其他东西首先更改,并在数据更改时提醒用户。大多数与用户交互的应用程序都应该使用乐观锁定。但是,它确实要求用户注意并注意更新的值。

请注意,由于乐观锁很少(并且在短时间内)使用锁,因此它通常不会与使用悲观锁的单独进程发生冲突。悲观的锁定应用程序会阻止乐观的应用程序更新锁定的行,但不会读取它们。
另请注意,这通常不适用于批量更新,因为它几乎没有用户交互(如果有的话)。


tl;博士

不要在读取时锁定您的行。只需将旧值与应用程序上次读取的值进行比较,如果不匹配则拒绝更新(并提醒用户)。培训您的用户做出适当的回应。

【讨论】:

  • 虽然这不是我一直在寻找的答案,但我将此答案标记为正确。我简直不敢相信,在 DB2 中不费吹灰之力就能完美运行的东西在 PostgreSql 中是不可能的:-(
  • @agad - 我有理由相信您的 DB2 应用程序 1] 实际上没有按照您的预期做(至少不完全,请参阅我对您的问题的第一条评论),并且 2] 没有采取行动用户的最大利益(锁定争用的可能性太大)。此外,SELECT FOR UPDATE 不应用于滚动行 - 您当前的问题是您有两个进程为同一行执行此操作,这当然会发生冲突。但是在滚动过程中先执行SELECT 没有锁定会让你再次回到这个答案......
  • 应用程序已经工作了几年(并且仍然有效)。这次没有人抱怨它。用户很高兴,他们看到了实际数据并知道是否有人正在更新它。使应用程序也可以在 Postgre 上运行的请求引发的问题。当前应用程序在尝试针对 Postgre 运行时导致 db 不一致,因为 2 个用户同时更新了 db。
  • @agad - 根据您的实际应用程序设计,他们可能看不到“实际”数据。例如,如果您滚动游标,CURSOR STABILITY 在获取下一行时实际上会释放锁(返回多行实际上只锁定最后一行)。它也不会阻止您的应用程序在另一个进程更新它之后读取一行,但新数据提交之前(它会看到旧数据)。恶劣的比赛条件。如果您将其作为网页/通过网络提供,情况会变得更糟,因为您在收到响应时没有线索......
猜你喜欢
  • 1970-01-01
  • 2014-01-05
  • 1970-01-01
  • 2010-09-17
  • 2012-02-15
  • 2019-06-07
  • 2015-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多