REPEATABLE_READ隔离是否会阻止非范围查询的插入?

时间:2013-07-31 01:36:47

标签: sql transactions sql-insert transaction-isolation

给出以下SQL伪代码:

... open transaction ...
SELECT * FROM users WHERE users.id = 5
... some calculation here ...
INSERT INTO users (id) VALUES (5)
... commit transaction ...

REPEATABLE_READ isolation是否可以防止并发连接在查询和插入之间插入Users[id=5],或者我是否需要使用SERIALIZABLE隔离?

注意:如果可能,我正在寻找与数据库无关的解决方案。如果没有,请提供来自多个数据库的经验证据,以便我们可以反对某种行业共识。

更新:原始问题包含使用范围锁定的错误查询。由于此问题的目的是关注需要范围锁定的查询,因此我更新了问题。

2 个答案:

答案 0 :(得分:3)

REPEATABLE_READ不会阻止该表格。它保证事务在任何时候都看到相同的行。让我通过一个例子解释一下:

Time  Transaction 1                    Transaction2
 1    Begin Tx 1                        
 2                                     Begin Tx 2
 4    Select count(*) from my_tab;        
 5                                     Select count(*) from my_tab;
 6    Insert into ... my_tab;
 7    Commit;
 8                                     Select count(*) from my_tab;
 9                                     Insert into ... my_tab;
 10                                     Select count(*) from my_tab;
 11                                    Commit;
 12   Begin Tx3
 13   Select count(*) from my_tab;

如果my_tab有10行,则计数结果为:

  • 时间4:10行
  • 时间5:10行
  • 时间8:10行,因为如果事务模式为read_commited,则表处于repeateble_read模式,也将为10行。但是如果将Tx设置为read_uncommited,则行数将为11。
  • 时间10:由于它处于reapeateble读取模式,因此计数将为11行(10个原件加上当前事务中的插入之一)。如果tx模式是read_commited,则行数将为12(十个原件加上tx1的一个插入和当前tx的一个插入)。
  • 时间13:此处所有交易模式的行数均为12。

在可序列化模式下,每个事务都以单一模式执行,因此当事务不以提交或回滚结束时,没有一个事务可以启动。如果这种模式保证数据库的一致性有严重的性能问题。

修改

来自http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt中托管的SQL-92标准版(第67和68页)(从维基百科获得):

  

SQL事务的隔离级别为READ UNCOMMITTED,   READ COMMITTED,REPEATABLE READ或SERIALIZABLE。隔离   SQL事务的级别定义了操作的程度   SQL事务中的SQL数据或模式的影响会受到影响   受影响并可能影响对SQL数据或模式的操作   在并发SQL事务中。 SQL的隔离级别   事务默认为SERIALIZABLE。级别可以明确   由。设置。

     

在隔离级别执行并发SQL事务   SERIALIZABLE保证可序列化。可序列化的exe-   cution被定义为执行同意的操作   正在执行产生与之相同效果的SQL事务   一些串行执行那些相同的SQL事务。连续执行   cution是每个SQL事务执行完成的一个   在下一个SQL事务开始之前。

     

隔离级别指定可能发生的现象类型   在并发SQL事务的执行期间。下列   现象是可能的:

     

1)P1("脏读"):SQL事务T1修改一行。 SQL-       然后,事务T2在T1执行COMMIT之前读取该行。       如果T1然后执行ROLLBACK,T2将读取一行       从来没有承诺,因此可能被认为从来没有       存在。

     

2)P2("不可重复读"):SQL事务T1读取一行。 SQL-       事务T2然后修改或删除该行并执行       一个COMMIT。如果T1然后尝试重新读取该行,它可能会收到       修改后的值或发现该行已被删除。

     

3)P3(" Phantom"):SQL事务T1读取行集N       满足一些。然后是SQL事务T2       执行生成一行或多行的SQL语句       满足SQL事务T1使用的。如果       然后,SQL事务T1重复使用相同的初始读取       ,它获得了不同的行集合。

     

四个隔离级别保证每个SQL事务都会    完全执行或根本不执行,并且不会进行任何更新    丢失。隔离级别在现象方面是不同的    P1,P2和P3。表9," SQL事务隔离级别和    三种现象"指定可能的现象,而不是    对于给定的隔离级别可能。

           Level                P1         P2         P3
    ---------------------------------------------------------
    | READ UNCOMMITTED     | Possible | Possible | Possible |
    ---------------------------------------------------------
    | READ COMMITTED       | Not      |          |          |
    |                      | Possible | Possible | Possible |
    ---------------------------------------------------------
    | REPEATABLE READ      | Not      | Not      |          |
    |                      | Possible | Possible | Possible |
    ---------------------------------------------------------
    | SERIALIZABLE         | Not      | Not      | Not      |
    |                      | Possible | Possible | Possible |
    ---------------------------------------------------------------
    |                                                             |
    | Note: The exclusion of these phenomena or SQL-transactions  |
    |       executing at isolation level SERIALIZABLE is a        |
    |       consequence of the requirement that such transactions |
    |       consequence of the be serializable.                   |
    ---------------------------------------------------------------

编辑2

好的,您对锁和块感兴趣,此时RDMS提供程序如何实现这一点可能会有所不同(SQL Server通过示例太奇怪了)但是从大局来看这可能很有用,以下说明适用当您尝试修改/读取相同的数据(行)时。 RDMS执行事务时有两种类型的锁:

  1. 共享锁:这种锁允许其他事务访问数据。例如,许多交易可以同时读取相同的数据。
  2. 独占锁:这种锁阻止资源避免其他事务访问它,例如两个事务不能同时修改相同的数据,尝试修改资源的事务必须检查是否存在对资源没有排他性锁定。如果没有对资源的独占锁定,那么事务将获得独占锁定,直到它释放锁定,没有任何一个事务可以获得锁定,并且必须等待释放独占锁定。
  3. 这里的第二点是:锁定了什么?通常有两种类型的锁:

    1. 表级锁:当事务试图修改数据时,事务获得所有表的独占锁,其他事务必须等待事务释放锁。
    2. 行级别锁定:当事务尝试修改数据时,事务获取事务中涉及的行的独占锁定,其他想要修改相同行的子集的事务必须等到第一个事务结束。
    3. 关于死锁的注意事项,想象一下以下情况:

      Time  Transaction 1                        Transaction 2
       1    Begin Tx 1                        
       2                                         Begin Tx 2
       4    Update table X set... where id = 5        
       5                                         Update table X set ... where id = 5;
       6    Update table X set... where id = 6
       7    Commit;
       8                                         Commit;
      

      如果数据库配置为行级锁定,则事务2将在时间5中等待,直到时间7,因为事务1首先获得独占锁定。现在想象下面的例子:

      Time  Transaction 1                        Transaction 2
       1    Begin Tx 1                        
       2                                         Begin Tx 2
       4    Update table X set... where id = 5        
       5                                         Update table X set ... where id = 6;
       6    Update table X set... where id = 6
       7                                         Update table X set ... where id = 5;
       8    Commit;
       9                                         Commit;
      

      这种情况被称为“死锁”'因为在时间6中,事务1将等待事务2在时间5上获得的锁定释放,但在时间7中,事务2 mus等待时间4中事务1获得的锁定。不同的RDBMS管理死锁以不同的方式举例来说,带有InnoDB的MySQL将为事务2引发死锁异常,让事务1完成。

      有一些有趣的文章:

      祝你好运

答案 1 :(得分:1)

没有。 REPEATABLE_READ隔离级别仅保证在同一事务中重复SELECT时将看到相同的数据,这意味着(SQL)实现可能会隐藏其他事务执行的事务插入。

能够阻止插入的唯一隔离级别是SERIALIZABLE,因为它保证如果您同时运行多个事务,结果就好像它们按特定顺序按顺序运行。每种情况下的具体顺序都没有指定也没有报告,但保证存在一个产生相同最终结果的顺序。

问题的一个条件是数据库不可知(我假设高级答案),所以我不会提到不同的隔离级别如何在不同的数据库实现中工作或它们如何转换为锁。