数据库读锁定

时间:2017-01-30 20:24:03

标签: spring oracle concurrency transactions locking

我有一个用例,我需要在一个事务中执行以下操作:

  1. 开始交易
  2. 将项目插入表格
  3. 选择表格中的所有项目
  4. 将所选项目转储到文件中(此文件已版本化,另一个程序始终使用最新版本)
  5. 如果以上所有事情都成功,请提交事务,如果没有,则回滚。
  6. 如果两个交易几乎同时开始,则可能在第一个交易A 提交已插入表格的内容之前(步骤4),第二个交易B 已经执行了SELECT操作(步骤2),其结果不包含第一个交易所插入的项目(因为它尚未由 A 提交,因此 B 不可见。在这种情况下,当 A 完成时,它将正确地转储包含其插入项的文件 File1 。稍后, B 结束,它将转储另一个文件 File2 ,其中仅包含其插入的项目,但不包含由 A 插入的文件。由于 File2 更新,我们将使用 File2 。问题是 File2 不包含A插入的项目,即使此项目在DB 中也很好。

    我想知道当事务在表中插入某些内容直到其提交或回滚时,锁定表的read(SELECT)是否可行解决此问题,如果是,如何在 Spring with Oracle as DB 中实现此锁定。

3 个答案:

答案 0 :(得分:1)

您需要在事务之间进行某种同步:

  1. 开始交易
  2. 获取锁定以阻止其他会话中的交易继续或等到其他会话中的交易完成
  3. 将项目插入表格
  4. SELECT ......
  5. ......
  6. 提交并解除锁定
  7. 最简单的方法是使用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_READSERIALIZABLE隔离级别:

  

REPEATABLE_READ表示脏读和的常量   防止不可重复的读取;可以发生幻像读取。这个   level禁止事务以未提交的方式读取行   它的变化,它也禁止一个人的情况   事务读取一行,第二个事务改变行,并且   第一个事务重新读取行,第二个事务获取不同的值   时间(a"不可重复读")。

     

SERIALIZABLE一个常量,指示脏读,不可重复   读取和幻像读取被阻止。这个级别包括   ISOLATION_REPEATABLE_READ禁止并进一步禁止   一个事务读取满足WHERE的所有行的情况   条件,第二个事务插入满足该条件的行   WHERE条件,并且第一个事务重新读取相同的内容   条件,检索额外的"幻像"在第二次阅读中排。

serializablerepeatable 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()

来获取和释放锁定