Hibernate + PostgreSQL:使用独占锁进行延迟加载

时间:2013-03-23 00:59:38

标签: java spring hibernate postgresql

在这一次头发上撕了几天头发。我们在使用独占数据库锁时遇到了一些问题,导致我们的生产系统出现性能问题一段时间了。我能够仔细研究它,并注意到持有独占锁的查询是由Hibernate的延迟加载生成的选择。

我们正在使用Spring事务管理,@Transactional( readOnly= "true")是在服务入口点定义的。我们使用会话每请求模型和映射到传输对象的实体。读取数据库默认隔离级别已提交。 JDBC驱动程序配置为read committed。我使用以下方法检查了实际交易的隔离级别:

select current_setting('transaction_isolation')

返回read committed。我们使用JPA来配置Hibernate映射。我们没有在任何地方明确升级交易。在这个特定的事务中,我们只运行select语句。打开Hibernate SQL日志记录我没有看到任何这些:

select ... for update

仅记录简单的select语句。

这似乎是两件事之一。我对read committed的理解是完全关闭的,读取提交的隔离级别应该导致在执行选择的事务期间保持独占的行级锁定。或者正在发生其他事情并错误地升级事务所持有的锁。

任何帮助都将不胜感激。

修改1

好的,在这一条路上走了很长的路。事实证明这与锁定无关。我用来检测锁的查询已经过时,并且显示锁定类型为“virtualxid”。一些挖掘告诉我们,virtualxid是每个事务自身锁定的锁,因为PostgreSQL的内部原因与此讨论没有密切关系。我们cron'd另一个监控查询测试真正的独占锁,还没有见过。

以下是我们用于监视“virtualxid”锁的查询,此时更像是一个长时间运行的查询监视器:

SELECT pg_stat_activity.datname, pg_locks.mode, pg_locks.locktype, pg_locks.granted, pg_stat_activity.usename,pg_stat_activity.query,age(now(),pg_stat_activity.query_start) AS "age", pg_stat_activity.pid
FROM pg_stat_activity,pg_locks
LEFT OUTER JOIN pg_class ON (pg_locks.relation = pg_class.oid)
WHERE
  age(now(),pg_stat_activity.query_start) > interval '1 minute' AND
  pg_stat_activity.datname <> 'postgres' AND
  pg_locks.pid=pg_stat_activity.pid AND
  pg_stat_activity.query not like '%autovacuum%' AND
  pg_stat_activity.query not like '%COPY%stdout%'
  order by query_start;

这是我们得到的一些输出:

<redacted> | ExclusiveLock | virtualxid | t       | <redacted> | SELECT current_timestamp | 01:03:51.809594 | 22578

一个简单的选择current_timestamp运行超过一个小时!!!

无论如何,对于那些感兴趣的人来说,看起来这些神秘的长时间运行的查询偶尔会耗尽我们的数据库连接池。所以我们提高了连接池的限制,现场网站又恢复了嗡嗡声。我们已经在关键流程上设置了应用程序端超时和重试逻辑,以处理偶尔的打嗝。而这些天我们通常至少有一个数据库线程卡住了这些奇怪的执行查询之一。绝对不理想:(

我们将尝试启用基于成本的自动吸尘器,看看这是否有助于解决问题。

编辑2

这一次结果是一段漫长的旅程,可能就在它的尽头。为了响应这种行为,除了上面提到的数据库查询监控之外,我们还支持了批处理错误报告。结合一些智能超时,这使我们能够将特定的应用程序用例与长时间运行的数据库查询相关联。这使我们能够对生产中出现的错误做出反应,以防止特定用法挂起JVM节点。

我们还能够解决为什么一个进程中长时间运行的只读TX会挂断连接到同一数据库的其他进程的问题。这是事情变得有点奇怪的地方。我们使用hibernate-memcached将hibernate的二级缓存移动到连接到同一数据库的所有Java进程的共享memcached服务器。每当我们得到奇怪的挂起行为时,JVM进程中就会有大量的memcached客户端线程。

删除hibernate-memcached模块后,回到ehcache进行二级缓存,我们注意到奇怪的多JVM衰弱的悬挂消失了。我们仍然会偶尔收到一封电子邮件,告诉我们TX内部发生的事情比应该发生的要多一些。我们仍然偶尔会出现单个JVM进程,因为它有太多的这些长TX正在大规模进行。但是我们不再看到一个JVM中的进程以某种方式影响其他JVM。以前我们会看到其他节点没有响应,直到我们终止显示不良TX行为的初始节点。

哪个没有意义。但后来这个问题从未发生过:)

- 蒂姆

2 个答案:

答案 0 :(得分:1)

首先,你需要的是Jim mlodgenski和Bruce Momjian在JBoss World 2009中提出的优秀的 Scaling Hibernate Applications with Postgres ,以解决Hibernate和PostgreSQL最常见的问题(缓存,复制,连接池等)。哟可以找到它here

然后,如果您在使用延迟加载时遇到问题,可以在纯SQL中发送直接查询:

String SQL_QUERY = "SELECT insurance_name, id, invested_amount, avg(i...
                 + "invested_amount - avg(invested_amount) OVER(PARTI...
                 + "FROM insurance ";
Query query = session.createSQLQuery(SQL_QUERY)
                    .addScalar("insurance_name", Hibernate.STRING)
                    .addScalar("id", Hibernate.LONG)
                    .addScalar("invested_amount", Hibernate.LONG)
                    .addScalar("a", Hibernate.DOUBLE)
                    .addScalar("diff", Hibernate.DOUBLE);

答案 1 :(得分:0)

hibernate-memcached似乎是这个问题的根本原因。从我们的系统中删除hibernate-memcached使我们所有的问题,如果不消失,至少开始像正常的数据库问题一样,你希望找到增强和扩展应用程序。

我并不是想对hibernate-memcached说些什么。我们成功地将这个项目用于生产一年多而没有发生任何事故。更有可能与我们系统特有的其他东西发生冲突,而hibernate-memcached对我们来说只是最容易改变。

相关问题