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

2022-08-31 08:55:28

我读过关于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 块上的锁定(通过使用选择查询选择)

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

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


答案 1

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

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

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

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

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

使用休眠:

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

哪里

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

休眠配置取自此处(抱歉,它是西班牙语)。

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

等等...


答案 2

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

MVCC允许并发修改,但代价是内存中的开销增加(因为它必须维护相同数据的不同版本)和计算(REPETEABLE_READ级别,您不能丢失更新,因此它必须检查数据的版本,就像Hiberate对Boodick Locking所做的那样)。

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

  • 读取数据时是否采用锁,以及请求的锁类型。

  • 读锁定的保持时间。

  • 读取操作是否引用由其他事务修改的行:

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

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

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

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

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

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

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

  • READ_COMMITED:

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

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


推荐