如何在 Hibernate 中获取共享锁和独占锁
How acquiring shared and exclusive locks works in Hibernate
我试图了解 READ COMMITED 和 READ UNCOMMITED 隔离级别在 Hibernate 中的工作方式,需要一些解释。
有 2 个线程 THR1 和 THR2 都执行相同的事务方法(Spring Transactional 注释隔离级别设置为 READ COMMITED)。相应地,将创建的事务命名为这些线程 TRA1 和 TRA2。事务方法如下所示:
public void updateOrSavePreference(String name, String value) {
Preference preferenceToUpdate = findPreferenceUsingNamedQuery(name); // line 1. shared read lock acquired (uses HibernateTemplate.findByNamedQueryAndNamedParam(...))
if (preferenceToUpdate != null) { // line 2.
preferenceToUpdate.setValue(value); // line 3. exclusive write lock acquired (actually I use the HibernateTemplate.merge(...) method
// instead a setter because the entity type is immutable, but it seems irrelevant for this example)
} else { // line 4.
HibernateTemplate.save(preferenceToUpdate); // line 5. exclusive write lock acquired
}
}
首选项 class 注释为 Entity(optimisticLock = OptimisticLockType.NONE) 以为此实体强制执行 2PL 模型(我错了吗?)。我使用 Oracle 数据库。
考虑以下场景:
假设线程 THR1 进入第 1 行并查询一个对象。如果我没理解错的话,这个线程创建的事务TRA1为被查询的实体获取了一个共享读锁。那么,如果 THR2 线程进入第 3 行,试图为该实体获取独占写锁,那么在 TRA1 释放读锁之前,THR2 不应该被阻塞吗?
假设线程 THR1 进入第 3 行并为实体获取排他写锁(排他锁一直保持到 TRA1 transaction completes)。然后,THR2 线程进入第 1 行并尝试查询该实体。不应阻止 THR2,因为 TRA2 事务试图获取读锁,而其他事务 TRA1 持有该实体的独占写锁?
如果我针对 READ UNCOMMITED 隔离级别重现第 2 点的场景,即使在 refreshing 之后,执行 TRA2 事务的 THR2 也看不到 THR1 在 TRA1 事务中所做的更改再次查询实体("evaluate expression" 处于调试状态)。为什么?
技术上读提交可以通过设置读锁来实现。但不一定。如果您的 DBMS 支持 MVCC,您总是读取已提交的数据(除了您自己的事务中更改的数据)而无需设置锁。
所以我怀疑您使用 oracle、mysql (INNODB) 或 postgres 进行测试?所有这些 DBMS 默认都支持 MVCC,因此它们从不设置共享读锁。
由于您使用的是 Oracle "the MVCC" 数据库,即使您在您的实体中配置了 2PL 协议,也不会实施该协议。如果您想了解您的 DBMS 在本机语句中真正做了什么,只需激活本机语句的输出,就像您在 persistence.xml:
中所做的那样
<property name="hibernate.show_sql" value="true" />
也许你也应该看看transaction-isolation-levels-relation-with-locks-on-table
或首先在:locks and oracle
我试图了解 READ COMMITED 和 READ UNCOMMITED 隔离级别在 Hibernate 中的工作方式,需要一些解释。
有 2 个线程 THR1 和 THR2 都执行相同的事务方法(Spring Transactional 注释隔离级别设置为 READ COMMITED)。相应地,将创建的事务命名为这些线程 TRA1 和 TRA2。事务方法如下所示:
public void updateOrSavePreference(String name, String value) {
Preference preferenceToUpdate = findPreferenceUsingNamedQuery(name); // line 1. shared read lock acquired (uses HibernateTemplate.findByNamedQueryAndNamedParam(...))
if (preferenceToUpdate != null) { // line 2.
preferenceToUpdate.setValue(value); // line 3. exclusive write lock acquired (actually I use the HibernateTemplate.merge(...) method
// instead a setter because the entity type is immutable, but it seems irrelevant for this example)
} else { // line 4.
HibernateTemplate.save(preferenceToUpdate); // line 5. exclusive write lock acquired
}
}
首选项 class 注释为 Entity(optimisticLock = OptimisticLockType.NONE) 以为此实体强制执行 2PL 模型(我错了吗?)。我使用 Oracle 数据库。
考虑以下场景:
假设线程 THR1 进入第 1 行并查询一个对象。如果我没理解错的话,这个线程创建的事务TRA1为被查询的实体获取了一个共享读锁。那么,如果 THR2 线程进入第 3 行,试图为该实体获取独占写锁,那么在 TRA1 释放读锁之前,THR2 不应该被阻塞吗?
假设线程 THR1 进入第 3 行并为实体获取排他写锁(排他锁一直保持到 TRA1 transaction completes)。然后,THR2 线程进入第 1 行并尝试查询该实体。不应阻止 THR2,因为 TRA2 事务试图获取读锁,而其他事务 TRA1 持有该实体的独占写锁?
如果我针对 READ UNCOMMITED 隔离级别重现第 2 点的场景,即使在 refreshing 之后,执行 TRA2 事务的 THR2 也看不到 THR1 在 TRA1 事务中所做的更改再次查询实体("evaluate expression" 处于调试状态)。为什么?
技术上读提交可以通过设置读锁来实现。但不一定。如果您的 DBMS 支持 MVCC,您总是读取已提交的数据(除了您自己的事务中更改的数据)而无需设置锁。
所以我怀疑您使用 oracle、mysql (INNODB) 或 postgres 进行测试?所有这些 DBMS 默认都支持 MVCC,因此它们从不设置共享读锁。
由于您使用的是 Oracle "the MVCC" 数据库,即使您在您的实体中配置了 2PL 协议,也不会实施该协议。如果您想了解您的 DBMS 在本机语句中真正做了什么,只需激活本机语句的输出,就像您在 persistence.xml:
中所做的那样<property name="hibernate.show_sql" value="true" />
也许你也应该看看transaction-isolation-levels-relation-with-locks-on-table 或首先在:locks and oracle