120

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

以下是事务隔离中可能出现的三种现象
Dirty Read - no lock
Nonrepeatable Read - no dirty read as lock on commited data
Phantom Read - lock on block of sql(通过select查询选择)

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

PS:我已经浏览了oracle 中的隔离级别中的链接,但它们看起来很笨拙并且谈论特定于数据库的内容

4

3 回答 3

177

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

例如,您有 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 < 20A插入aField值在10到20之间的数据,然后B再次读取数据并得到不同的结果。
  • SERIALIZABLE - 锁定一个完整的表(在其上触发 Select 查询)。这意味着,B 读取数据并且没有其他事务可以修改表上的数据。这是处理数据的最安全但最慢的方式。另外,由于一个简单的读操作锁定了表,这会导致生产上的严重问题:假设 T 表是一个 Invoice 表,用户 X 想知道当天的发票,用户 Y 想创建一个新发票,所以当 X 执行发票的读取时,Y 不能添加新的发票(当涉及到钱时,人们真的很生气,尤其是老板)。

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

使用 JDBC,您可以使用Connection#setTransactionIsolation.

使用休眠:

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

在哪里

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

Hibernate 配置取自这里(对不起,它是西班牙语)。

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

不断...

于 2013-04-23T06:26:52.147 回答
10

正如 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事务回滚时,T1事务版本2是否不应用于A记录也没关系。之后,如果提交了 T2 事务,则 T2 事务版本 3 将应用于 A 记录。如果 T1 事务在 T2 事务之前提交,则 A 记录更改为 2,然后在提交 T2 事务时更改为 3。最终的数据库状态与独立执行每个事务的状态相同,对其他事务没有任何影响。因此,它满足 ACID 属性。这种方法称为多版本并发控制(MVCC)。

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

在 2PL事务隔离级别中控制以下内容

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

  • 持有读锁的时间。

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

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

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

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

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

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

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

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

  • READ_COMMITED:

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

  • SERIALIZABLE:范围锁放置在与事务中执行的每个语句的搜索条件匹配的键值范围内。[...] 范围锁一直保持到事务完成。

于 2016-01-26T17:18:59.540 回答
5

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

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

如何设置锁取决于所谓的事务隔离级别,它的范围可以从根本不支持事务到支持强制执行非常严格的访问规则的事务。

事务隔离级别的一个示例是 TRANSACTION_READ_COMMITTED,它不允许访问一个值,直到它被提交之后。换言之,如果事务隔离级别设置为 TRANSACTION_READ_COMMITTED,则 DBMS 不允许发生脏读。接口 Connection 包括五个值,它们代表您可以在 JDBC 中使用的事务隔离级别。

于 2015-08-04T19:49:54.367 回答