我在2台不同的机器上运行了2个程序。
每个程序都有一个名为updateRecord的方法,它执行以下两项操作:
1.对特定记录执行SELECT查询Z
2.对同一记录执行UPDATE查询。
如果这两个查询在同一个事务中(beginTransaction和commitTransaction之间),它是否保证正确执行?
即,以下操作序列失败会成功执行吗?
OR
答案 0 :(得分:3)
您的程序在选择记录时需要锁定记录 - 例如使用SELECT FOR UPDATE语法。这样,记录将被锁定,直到UPDATE完成。
答案 1 :(得分:2)
如前所述,使用SELECT ... FOR UPDATE
有助于锁定行,直到提交(或回滚)事务为止。
您不需要两台机器来测试它。通过使用两个不同的会话(例如,通过运行两个不同的SQL * Plus实例)并在两个会话上以特定顺序同时运行查询,您将能够重现并发问题(如果有)。
在这种情况下,你可以运行:
Session1: SELECT z AS sel_z -- sel_z = 0
Session1: UPDATE z = sel_z + 1
Session2: SELECT z AS sel_z -- (1) sel_z = 0 because Session1 is uncommitted
Session2: UPDATE z = sel_z + 1
Session1: COMMIT
Session2: COMMIT
Session1: SELECT z AS sel_z -- sel_z = 1
Session2: SELECT z AS sel_z -- sel_z = 1
问题出在(1),Session2没有看到Session1改变的值,因为它们没有被提交。
我的建议是不要考虑改变TX隔离级别,考虑锁定适当的资源。
答案 2 :(得分:1)
两台机器永远不会使用相同的交易。如果SELECT& UPDATE在存储过程中执行,它们将在同一事务中。如果SELECT& UPDATE查询作为单独的语句运行,然后可能出现以下情况:
根据数据库隔离级别,机器#2的选择可能是在运行机器#1的UPDATE之前查看数据。 IIRC,默认情况下就是这种情况。
这是411 on Oracle's Isolation Levels, per AskTom。
对于MySQL,请使用SET TRANSACTION
命令。有关MySQL隔离级别支持的更多信息,请参阅this link。