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


105

我已经阅读了大约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)

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

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

PS:我已经看过oracle中“ 隔离级别”中的链接,但是它们看上去很笨拙,并讨论了特定于数据库的问题


3
这完全取决于数据库。不同的数据库可能对隔离级别使用不同的算法。有些可能使用MVCC(选择查询没有锁定),有些使用严格的2相锁定(共享和互斥锁定)。
brb tea

Answers:


157

我想了解每个事务隔离对表的锁定

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

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

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

使用JDBC时,您可以使用进行定义Connection#setTransactionIsolation

使用休眠:

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

哪里

  • 1:读未提交
  • 2:已读
  • 4:可重复读取
  • 8:可序列化

休眠配置是从这里获取的(抱歉,它是西班牙语的)。

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

还有……


docs.oracle.com/cd/B12037_01/server.101/b10743/consist.htm 为Oracle添加的一个方法:可以在事务开始时使用以下语句之一来设置事务的隔离级别:SET TRANSACTION ISOLATION级别已提交;可设置交易隔离级别;设置交易只读
学习者2014年

2
此外,为了节省使用SET TRANSACTION语句开始每个事务的网络和处理成本,您可以使用ALTER SESSION语句为所有后续事务设置事务隔离级别:ALTER SESSION SET ISOLATION_LEVEL SERIALIZABLE; ALTER SESSION SET ISOLATION_LEVEL读取已提交;
学习者2014年

12
关于可重复读取-我认为有一个更好的示例来演示它,如下所示:B启动一个事务,在aField> 10和aField <20的sql块上读取数据,该数据被锁定,直到事务结束。A尝试更新该数据,但由于锁定而等待。现在,当B将在同一事务中再次读取该数据时,由于它已锁定,因此可以保证读取相同的数据。如我错了请纠正我。
BornToCode 2015年

1
@LuiggiMendoza作为一般概念,隔离级别仅与脏读不可重复读幻影行有关。锁(S2PL)或MVCC是针对不同供应商的实现。
brb tea

4
@LuiggiMendoza-我不准确,应该是这样-B读取的数据没有更改,但是B所做的选择可能返回更多行。这是因为 A释放它们之前,A 不能修改B已经读取的行。但是,A可以插入符合where条件的新行(因此,下次A执行一次选择时,它将获得更多行的不同结果-幻像读取)。
BornToCode 2015年

9

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

CUBRID(开源RDBMS)解释了这两种算法的思想:

  • 两相锁定(2PL)

第一个是当T2事务尝试更改A记录时,它知道T1事务已经更改了A记录并等待直到T1事务完成,因为T2事务无法知道T1事务将被提交还是滚动背部。此方法称为两相锁定(2PL)。

  • 多版本并发控制(MVCC)

另一个是允许它们中的每个(T1和T2事务)都有自己的更改版本。即使当T1事务将A记录从1更改为2时,T1事务仍将原始值保留为1,并写入A记录的T1事务版本为2。然后,随后的T2事务将A记录更改从1到3,而不是从2到4,并且写道A记录的T2事务版本是3。

回滚T1事务时,是否将2(T1事务版本)不应用于A记录并不重要。此后,如果提交了T2事务,则将3(T2事务版本)应用于A记录。如果在T2事务之前提交了T1事务,则将A记录更改为2,然后在提交T2事务时更改为3。最终数据库状态与独立执行每个事务的状态相同,而不会影响其他事务。因此,它满足ACID属性。此方法称为多版本并发控制(MVCC)。

MVCC允许并发修改,但要以增加内存开销(因为它必须维护相同数据的不同版本)和计算(在REPETEABLE_READ级别上)为代价,因此您不能丢失更新,因此它必须检查数据的版本,例如Hiberate与Optimistick Locking一起使用)。

在2PL 事务中,隔离级别控制以下各项

  • 读取数据时是否获取锁,以及请求什么类型的锁。

  • 读取锁的保持时间。

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

    • 阻塞直到释放该行上的排他锁。

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

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

选择事务隔离级别不会影响为保护数据修改而获取的锁。无论为该事务设置的隔离级别如何,事务始终会获得对其修改的任何数据的排他锁,并保持该锁直到事务完成。对于读取操作,事务隔离级别主要定义保护级别,以免受其他事务所做的修改的影响。

较低的隔离级别提高了许多用户同时访问数据的能力,但增加了用户可能遇到的并发影响(例如脏读或更新丢失)的数量

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:范围锁放置在与事务中执行的每个语句的搜索条件匹配的键值范围内。[...]范围锁将保持到事务完成为止。


5

锁定始终在数据库级别执行:-

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

锁的设置方式取决于所谓的事务隔离级别,该级别可以从完全不支持事务到支持强制执行非常严格的访问规则的事务。

事务隔离级别的一个示例是TRANSACTION_READ_COMMITTED,它只有在提交值之后才允许访问该值。换句话说,如果将事务隔离级别设置为TRANSACTION_READ_COMMITTED,则DBMS不允许进行脏读。接口Connection包含五个值,这些值代表您可以在JDBC中使用的事务隔离级别。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.