我有一个用例,我需要在一个事务中执行以下操作:
如果两个交易几乎同时开始,则可能在第一个交易A 提交已插入表格的内容之前(步骤4),第二个交易B 已经执行了SELECT操作(步骤2),其结果不包含第一个交易所插入的项目(因为它尚未由 A 提交,因此 B 不可见。在这种情况下,当 A 完成时,它将正确地转储包含其插入项的文件 File1 。稍后, B 结束,它将转储另一个文件 File2 ,其中仅包含其插入的项目,但不包含由 A 插入的文件。由于 File2 更新,我们将使用 File2 。问题是 File2 不包含A插入的项目,即使此项目在DB 中也很好。
我想知道当事务在表中插入某些内容直到其提交或回滚时,锁定表的read(SELECT)是否可行解决此问题,如果是,如何在 Spring with Oracle as DB 中实现此锁定。
答案 0 :(得分:1)
您需要在事务之间进行某种同步:
最简单的方法是使用LOCK TABLE命令,至少在SHARE模式下(也可以使用SHARE ROW EXCLUSIVE或EXCLUSIVE模式,但对于这种情况它们太受限制)。
这种方法的优点是锁在提交或回滚时自动释放。
缺点是事实是,此锁可能会干扰系统中同时更新此表的其他事务,并可能降低整体性能。
另一种方法是使用DBMS_LOCK包。
此锁定不会影响明确使用该锁定的其他交易。
drawaback是这个包很难使用,在提交或回滚时没有释放锁,你必须在事务结束时明确释放锁,因此必须小心处理所有异常,否则就会出现死锁很容易发生。
另一个解决方案是创建一个"虚拟"其中包含单行的表格,例如:
CREATE TABLE my_special_lock_table(
int x
);
INSERT INTO my_special_lock_table VALUES(1);
COMMIT:
然后使用SELECT x FROM my_special_lock_table FOR UPDATE
或者 - 甚至更简单 - 在您的交易中简单UPDATE my_special_lock_table SET x=x
这将对此表中的行进行独占锁定,并仅同步此一个事务
缺点是另一个"虚拟"必须创建表。
但是这个解决方案不会影响系统中的其他事务,锁在提交或回滚时自动释放,并且是可移植的 - 它应该在所有其他数据库中工作,而不仅仅在Oracle中。
答案 1 :(得分:0)
使用spring的REPEATABLE_READ
或SERIALIZABLE
隔离级别:
REPEATABLE_READ表示脏读和的常量 防止不可重复的读取;可以发生幻像读取。这个 level禁止事务以未提交的方式读取行 它的变化,它也禁止一个人的情况 事务读取一行,第二个事务改变行,并且 第一个事务重新读取行,第二个事务获取不同的值 时间(a"不可重复读")。
SERIALIZABLE一个常量,指示脏读,不可重复 读取和幻像读取被阻止。这个级别包括 ISOLATION_REPEATABLE_READ禁止并进一步禁止 一个事务读取满足WHERE的所有行的情况 条件,第二个事务插入满足该条件的行 WHERE条件,并且第一个事务重新读取相同的内容 条件,检索额外的"幻像"在第二次阅读中排。
serializable
或repeatable read
,该组将受到保护,不会受到不可重复的读取:
connection 1: connection 2:
set transaction isolation level
repeatable read
begin transaction
select name from users where id = 1
update user set name = 'Bill' where id = 1
select name from users where id = 1 |
commit transaction |
|--> executed here
在这种情况下,update
将阻止,直到第一个交易完成。
很少使用较高的隔离级别,因为它们会同时降低可在数据库中工作的人数。在最高级别serializable
,报告查询会暂停所有更新活动。
答案 2 :(得分:0)
我认为你需要序列化整个交易。虽然SELECT ... FOR UPDATE可以工作,但它并没有真正为你买任何东西,因为你会选择所有行。您也可以使用DBMS_LOCK()
来获取和释放锁定