将 HSQLDB 与 Hibernate 和 Spring 结合使用时,我遇到了并发问题。我有两个线程访问同一个表。一个线程有一个长时间运行的读写事务,另一个线程经常以只读方式访问表,但只有一个短事务。主要问题是长时间运行的事务阻塞了较快的事务。
当检查 HSQLDB 的文档时,我发现了不同的并发模型。在这种情况下,MVLOCKS 或 MVCC 似乎应该对我有所帮助。
使用 MVLOCKS 时,这个问题似乎仍然普遍存在,而使用 MVCC 时,这个问题实际上是有效的。我的假设是,使用 MVLOCKS 也应该可以工作,我想了解为什么在这种情况下它不能工作?
我准备了一个显示问题的示例应用程序https://github.com/fridewald/hsqldb-test 它使用普通的休眠会话,因为否则我无法重现问题。
在这里你可以看到我如何在
application.properties
文件中将设置更改为mvlocks或mvcc
# this does not work, it blocks the reads. Why?
spring.datasource.url=jdbc:hsqldb:mem:testdb;hsqldb.tx=mvlocks
# this works
#spring.datasource.url=jdbc:hsqldb:mem:testdb;hsqldb.tx=mvcc
在这里您可以看到长期运行事务的部分
@Component
@Service
public class SlowUpdates {
...
@Scheduled(fixedRate = 10000)
public void slowlyUpdatingLamps() {
new Thread(new Runnable() {
public void run() {
log.info("try to slow update");
try (
Session session = sessionFactory.openSession();) {
session.beginTransaction();
long now = System.currentTimeMillis();
CriteriaBuilder lCriteriaBu = session.getCriteriaBuilder();
CriteriaQuery<Lamp> lQuery = lCriteriaBu.createQuery(Lamp.class);
Root<Lamp> lRoot = lQuery.from(Lamp.class);
lQuery.select(lRoot).where(lCriteriaBu.equal(lRoot.get("numberOfLamps"), 3));
log.info("Lamp read with findById(1L):");
List<Lamp> lamps = session.createQuery(lQuery).getResultList();
for (Lamp l : lamps) {
l.setLastUpdated(now);
session.persist(l);
log.info(l.toString());
}
session.flush();
// Wait 5 seconds and keep transaction open
Thread.sleep(5000);
List<Lamp> lamps2 = session.createQuery(lQuery).getResultList();
for (Lamp l : lamps2) {
l.setLastUpdated(now);
session.persist(l);
log.info(l.toString());
}
session.getTransaction().commit();
log.info("Session end");
} catch (Exception e) {
log.error("Error in slowlyUpdatingLamps", e);
}
}
}).start();
}
}
这里是尝试从表中读取的部分
public class FastReads {
...
@Scheduled(fixedRate = 500)
public void readLamps() {
log.info("try to fast read");
try (
Session session = sessionFactory.openSession();) {
session.beginTransaction();
session.setDefaultReadOnly(true);
log.info("Lamp read with findById(1L):");
Lamp lamp = session.get(Lamp.class, 1L);
log.info(lamp.toString());
session.getTransaction().commit();
} catch (Exception e) {
log.error("Error in readLamps", e);
}
}
}
我已经尝试为读取事务设置
session.setDefaultReadOnly(true);
,但这没有任何效果。是否有另一种方法告诉 hibernate 事务或会话是只读的,将被正确转发到 hsqldb?
在 hsqldb 数据库上尝试使用两个 sql 控制台的类似示例时,mvlocks 的行为符合预期。
Hibernate的只读模式不影响数据库事务。这是 Hibernate 级别的内部优化,可让您避免浪费资源来跟踪从数据库加载的对象的更改。交易最好尽可能短。如果您需要处理大量数据,那么进行多笔短交易比进行一笔长交易更有意义。