事务隔离级别与表上的锁相关

时间:2013-04-23 06:22:40

标签: java transactions isolation-level

我已经阅读了4个级别的隔离:

Isolation Level       Dirty Read    Nonrepeatable Read  Phantom Read  
READ UNCOMMITTED      Permitted       Permitted           Permitted
READ COMMITTED              --        Permitted           Permitted
REPEATABLE READ             --             --             Permitted
SERIALIZABLE                --             --              --

我想了解每个事务隔离在表上执行的锁

READ UNCOMMITTED - no lock on table
READ COMMITTED - lock on committed data
REPEATABLE READ - lock on block of sql(which is selected by using select query)
SERIALIZABLE - lock on full table(on which Select query is fired)

以下是事务隔离中可能发生的三种现象
脏读 - 没有锁定
不可重复读 - 没有脏读作为已提交数据的锁定
Phantom Read - 锁定sql块(使用select查询选择)

我想了解我们在哪里定义这些隔离级别:仅在jdbc / hibernate级别或在DB中

PS:我已经浏览了Isolation levels in oracle中的链接,但是它们看起来很笨拙并且谈论数据库特定的

3 个答案:

答案 0 :(得分:133)

  

我想了解每个事务隔离在表上执行的锁

例如,您有3个并发进程A,B和C. A启动事务,写入数据和提交/回滚(取决于结果)。 B只执行SELECT语句来读取数据。 C读取和更新数据。所有这些过程都在同一个表T上工作。

  • READ UNCOMMITTED - 表格无锁定。您可以在书写时读取表格中的数据。这意味着A写入数据(未提交),B可以读取此未提交的数据并使用它(出于任何目的)。如果A执行回滚,B仍然读取数据并使用它。这是处理数据的最快但最不安全的方式,因为这可能会导致数据漏洞不在物理相关的表中(是的,两个表在逻辑上可能在实际应用中没有物理相关= \)。
  • READ COMMITTED - 锁定已提交的数据。您可以读取仅提交的数据。这意味着A写入数据,B在A执行提交之前无法读取A保存的数据。这里的问题是C可以更新在B上读取和使用的数据,B客户端将没有更新的数据。
  • REPEATABLE READ - 锁定SQL块(使用select查询选择)。这意味着B在某些条件下读取数据,即WHERE aField > 10 AND aField < 20,A插入数据,其中aField值介于10和20之间,然后B再次读取数据并得到不同的结果。
  • SERIALIZABLE - 锁定一个完整的表(在其上触发Select查询)。这意味着,B读取数据并且没有其他事务可以修改表上的数据。这是处理数据的最安全但最慢的方法。此外,由于简单的读取操作锁定,这可能导致生产中的严重问题:假设T表是发票表,用户X想知道当天的发票和用户Y想要的创建一个新的发票,所以当X执行发票的读取时,Y不能添加新的发票(当它是关于钱的时候,人们真的很生气,尤其是老板)。
  

我想了解我们在哪里定义这些隔离级别:仅在JDBC / hibernate级别或在DB中

使用JDBC,您可以使用Connection#setTransactionIsolation来定义它。

使用Hibernate:

<property name="hibernate.connection.isolation">2</property>

其中

  • 1:READ UNCOMMITTED
  • 2:READ COMMITTED
  • 4:REPEATABLE READ
  • 8:SERIALIZABLE

Hibernate配置来自here(抱歉,它是西班牙语)。

顺便说一下,您也可以在RDBMS上设置隔离级别:

等等...

答案 1 :(得分:6)

正如brb tea所说,取决于他们使用的数据库实现和算法:MVCC或两阶段锁定。

CUBRID(开源RDBMS)explains这两种算法的想法:

  
      
  • 两相锁定(2PL)
  •   
     

第一个是当T2交易试图改变A记录时,   它知道T1交易已经改变了A记录和   由于T2交易等待T1交易完成   无法知道T1交易是否将被提交或滚动   背部。这种方法称为两相锁定(2PL)。

     
      
  • 多版本并发控制(MVCC)
  •   
     

另一个是允许他们每个人进行T1和T2交易   有自己的更改版本。即使T1交易有   将A记录从1改为2,T1事务离开了   原始值1原样并写入T1事务版本   A记录为2.然后,以下T2事务更改A   记录从1到3,而不是从2到4,并写入T2   A记录的交易版本为3。

     

回滚T1事务时,如果是2,则无关紧要   T1事务版本,不适用于A记录。后   即,如果T2事务已提交,则为3,T2事务   版本,将应用于A记录。如果T1交易是   在T2交易之前提交,A记录改为2,   然后在提交T2交易时到3。决赛   数据库状态与执行每个数据库的状态相同   独立交易,对其他交易没有任何影响。   因此,它满足ACID属性。调用此方法   多版本并发控制(MVCC)。

MVCC允许并发修改,代价是增加内存开销(因为它必须维护相同数据的不同版本)和计算(在REPETEABLE_READ级别,您可以放松更新,因此必须检查版本的数据,如Hiberate与Optimistick Locking)。

在2PL Transaction isolation levels control the following中:

  
      
  • 读取数据时是否进行锁定,以及请求的锁定类型。

  •   
  • 读锁的持续时间。

  •   
  • 是否引用由另一个事务修改的行的读取操作:

         
        
    • 阻止,直到释放行的独占锁定。

    •   
    • 检索语句或事务开始时存在的行的已提交版本。

    •   
    • 阅读未提交的数据修改。

    •   
  •   
     

选择事务隔离级别不会影响锁定   获得以保护数据修改。交易总是得到   对其修改的任何数据进行独占锁定并保持锁定直到   无论为哪个隔离级别设置,事务都会完成   那笔交易。对于读操作,事务隔离级别   主要定义保护水平免受影响   其他交易所做的修改。

     

较低的隔离级别会增加许多用户访问的能力   数据同时增加并发数量   用户可能会产生影响,例如脏读或丢失更新   相遇。

SQL Server中锁和隔离级别之间关系的具体示例(使用2PL,READ_COMMITTED_SNAPSHOT = ON时READ_COMMITED除外)

  • READ_UNCOMMITED:不发布共享锁以防止其他事务修改当前事务读取的数据。 READ UNCOMMITTED事务也不会被独占锁阻止,这会阻止当前事务读取已被修改但未被其他事务提交的行。 [...]

  • READ_COMMITED:

    • 如果READ_COMMITTED_SNAPSHOT设置为OFF(缺省值):使用共享锁来防止其他事务在当前事务运行读操作时修改行。共享锁还会阻止语句读取由其他事务修改的行,直到另一个事务完成为止。 [...]在处理下一行之前释放行锁。 [...]
    • 如果READ_COMMITTED_SNAPSHOT设置为ON,则数据库引擎使用行版本控制为每个语句显示在语句开头存在的数据的事务一致性快照。锁不用于保护数据免受其他事务的更新。
  • REPETEABLE_READ:共享锁定放在事务中每个语句读取的所有数据上,并保持到事务完成。

  • SERIALIZABLE:范围锁定位于与事务中执行的每个语句的搜索条件匹配的键值范围内。 [...]范围锁保持到交易完成。

答案 2 :(得分:4)

锁始终处于数据库级别: -

Oracle官方文档: - 为了避免在事务期间发生冲突,DBMS使用锁定机制来阻止其他人访问事务正在访问的数据。 (请注意,在自动提交模式下,每个语句都是一个事务,只保留一个语句的锁定。)设置锁定后,它将一直有效,直到提交或回滚事务为止。例如,DBMS可以锁定表的一行,直到对其进行更新为止。此锁定的作用是防止用户获取脏读,即在永久化之前读取值。 (访问尚未提交的更新值被视为脏读,因为该值可能会回滚到其先前的值。如果读取稍后回滚的值,则会读取无效值。 )

如何设置锁定由所谓的事务隔离级别决定,其范围从完全不支持事务到支持执行非常严格的访问规则的事务。

事务隔离级别的一个示例是TRANSACTION_READ_COMMITTED,它不允许在提交值之前访问该值。换句话说,如果事务隔离级别设置为TRANSACTION_READ_COMMITTED,则DBMS不允许发生脏读。接口Connection包含五个值,表示可以在JDBC中使用的事务隔离级别。