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

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

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)

below are the three phenomena which can occur in transaction isolation
肮脏读取-没有锁
Nonrepeatable Read - no dirty read as lock on committed data
Phantom Read -lock on block of sql (通过使用 select query 选择)

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

PS: 我已经看过了 神谕中的隔离级别中的链接,但是它们看起来很笨拙,而且是针对数据库的

118515 次浏览

I want to understand the lock each transaction isolation takes on the table

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

  • READ UNCOMMITTED - no lock on the table. You can read data in the table while writing on it. This means A writes data (uncommitted) and B can read this uncommitted data and use it (for any purpose). If A executes a rollback, B still has read the data and used it. This is the fastest but most insecure way to work with data since can lead to data holes in not physically related tables (yes, two tables can be logically but not physically related in real-world apps =\).
  • READCOMMITTED -锁定已提交的数据。您可以读取仅提交的数据。这意味着 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 表是一个发票表,用户 X 想知道当天的发票,用户 Y 想创建一个新的发票,所以当 X 执行读发票时,Y 不能添加一个新的发票(当涉及到钱时,人们会非常生气,尤其是老板)。

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

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

使用 Hibernate:

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

在哪里

  • 1: 阅读未承诺
  • 2: 阅读承诺
  • 4: REPEATABLE READ
  • 8: 序列化

Hibernate configuration is taken from 给你 (sorry, it's in Spanish).

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

还有很多很多。

锁总是在数据库级别采用。

来自 Oracle official document:

为了避免在事务期间发生冲突,DBMS 使用锁,即阻止其他人访问事务正在访问的数据的机制。(请注意,在自动提交模式中,每个语句都是一个事务,只有一个语句持有锁。)设置锁之后,它将保持有效,直到事务被提交或回滚。例如,DBMS 可以锁定表的一行,直到提交对其的更新为止。此锁的作用是防止用户获得脏读操作,即在永久化之前读取值。(访问尚未提交的更新值被认为是 色情读物,因为可以将该值回滚到之前的值。如果您读取了一个稍后回滚的值,那么您将读取了一个无效的值。)

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

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

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

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

  • Two-phase locking (2PL)

第一个是当 T2事务试图更改 A 记录时, 它知道 T1事务已经改变了 A 记录 waits until the T1 transaction is completed because the T2 transaction 不能知道 T1事务是提交还是滚动 这个方法叫做两阶段锁(2PL)。

  • 多版本并发控制

另一种方法是允许 T1和 T2事务中的每个事务 有他们自己的更改版本。即使当 T1事务 将 A 记录从1更改为2,则 T1事务将离开 原始值1,并写入 T1事务版本 然后,下面的 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 允许并发修改,代价是增加内存开销(因为它必须维护相同数据的不同版本)和计算(在 REPETEABLE _ READ 级别,你不能松懈更新,所以它必须检查数据的版本,就像 Hiberate 对 乐观锁定所做的那样)。

第二阶段:

  • Whether locks are taken when data is read, and what type of locks are requested.

  • 持有读锁的时间。

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

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

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

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

选择事务隔离级别不会影响 是为了保护数据修改而获得的 an exclusive lock on any data it modifies and holds that lock until 事务完成,而不管为 那个事务。对于读操作,事务隔离级别 主要定义保护的水平,从影响 其他交易所作的变更。

A lower isolation level increases the ability of many users to access 数据,但增加了并发的数量 效果 ,如脏读或丢失更新,用户可能 遭遇。

Concrete examples of the relation between locks and isolation levels in SQL Server (use 2PL except on READ_COMMITED with READ_COMMITTED_SNAPSHOT=ON)

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

  • READ _ COMMITED:

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

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