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

jhdbpxl9  于 2023-05-21  发布在  Java
关注(0)|答案(4)|浏览(133)

我读过关于隔离的四个层次:

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)

以下是在事务隔离中可能发生的三种现象
Dirty Read-无锁定
Nonrepeatable Read-没有脏读作为提交数据的锁
Phantom Read-锁在sql的块上(使用select query选择)

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

PS:我已经通过在Isolation levels in oracle的链接,但他们看起来笨拙和谈论数据库的具体

mqkwyuun

mqkwyuun1#

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

例如,您有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 query选择)。这意味着B在某种条件下读取数据,即WHERE aField > 10 AND aField < 20,A插入aField值在10和20之间的数据,然后B再次读取数据并获得不同的结果。
*SERIALIZABLE-对全表(触发Select查询的表)锁定。这意味着,B读取数据并且没有其他事务可以修改表上的数据。这是最安全但最慢的数据处理方式。此外,由于一个简单的读操作会锁定,这可能会导致生产上的严重问题:假设T表是一个Invoice表,用户X想要知道当天发票,而用户Y想要创建一个新的发票,因此当X执行发票的读取时,Y不能添加新的发票(当涉及到钱的时候,人们会非常生气,尤其是老板)。
我想了解我们在哪里定义这些隔离级别:仅在JDBC/hibernate级别或也在DB中

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

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

在哪里

  • 1:读取未提交
  • 2:阅读已提交
  • 4:可重复读取
  • 8:可串行化

Hibernate配置取自here(对不起,是西班牙语)。
顺便说一下,你也可以在RDBMS上设置隔离级别:

一遍又一遍

cidc1ykv

cidc1ykv2#

正如brb tea所说,这取决于数据库实现和他们使用的算法:MVCC或两相锁定。
CUBRID(开源RDBMS)explains这两个算法的思想:

  • 两相锁定(2PL)

第一种是当T2事务试图改变A记录时,它知道T1事务已经改变了A记录,并且等待直到T1事务完成,因为T2事务不知道T1事务将被提交还是回滚。这种方法被称为两相锁定(2PL)。

  • 多版本并发控制

另一个是允许T1和T2事务中的每一个都有自己的更改版本。即使当T1事务已经将A记录从1改变为2时,T1事务也保持原始值1不变,并且写入A记录的T1事务版本为2。然后,随后的T2事务将A记录从1变为3,而不是从2变为4,并且写入A记录的T2事务版本是3。
回滚T1事务时,T1事务版本2是否未应用于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中锁和隔离级别之间关系的具体示例(除了READ_COMMITTED_SNAPSHOT=ON的READ_COMMITTED上使用2PL)

  • READ_UNCOMMITED:不发出共享锁以防止其他事务修改当前事务读取的数据。READ UNCOMMITTED事务也不会被排他锁阻止,排他锁会阻止当前事务阅读已被其他事务修改但未提交的行。[...]
  • READ_COMMITED:
  • 如果READ_COMMITTED_SNAPSHOT设置为OFF(默认值):使用共享锁防止其他事务在当前事务运行读取操作时修改行。共享锁还阻止语句阅读由其他事务修改的行,直到其他事务完成。[...]在处理下一行之前释放行锁。[...]
  • 如果READ_COMMITTED_SNAPSHOT设置为ON,则数据库引擎使用行版本控制为每个语句提供数据的事务一致快照,就像语句开始时存在的那样。锁不用于保护数据不被其他事务更新。
  • REPETEABLE_READ:共享锁放置在事务中每个语句读取的所有数据上,并一直保持到事务完成。
  • 可序列化:范围锁放置在与事务中执行的每个语句的搜索条件相匹配的键值范围中。[...]范围锁一直保持到事务完成。
zsohkypk

zsohkypk3#

锁总是在数据库级别获取。
Oracle official document
为了避免事务期间的冲突,DBMS使用锁,用于阻止其他人访问事务正在访问的数据的机制。(请注意,在自动提交模式下,每个语句都是一个事务,锁只针对一个语句持有。)设置锁后,它将一直有效,直到事务被提交或回滚。例如,DBMS可以锁定表的一行,直到提交了对它的更新。此锁的作用是防止用户进行脏读,即在将值变为永久值之前阅读该值。(访问尚未提交的更新值被认为是“脏读”,因为该值可能会回滚到其先前的值。如果读取的值后来被回滚,则读取的值无效。)
如何设置锁是由所谓的事务隔离级别决定的,该级别的范围可以从根本不支持事务到支持执行非常严格的访问规则的事务。
事务隔离级别的一个示例是TRANSACTION_READ_COMMITTED,它不允许在提交值之前访问该值。换句话说,如果事务隔离级别设置为TRANSACTION_READ_COMMITTED,则DBMS不允许发生脏读。接口Connection包括五个值,它们表示可以在JDBC中使用的事务隔离级别

q9yhzks0

q9yhzks04#

关于READ COMMITTED -我认为一个更好的例子来证明它如下:
假设:数据a =10;螺纹A、B、C

  1. A执行A = A+1 ==》 A = 11 ;同时,B和C尝试更新该数据,但由于锁而等待。
  2. A提交数据后,B查询A = 11,在此期间,C更新A = A +1 = 12,这里的问题就是B再次查询A = 12,同一事务中两个查询的数据不一致
    如果我说错了请纠正我。

相关问题