在这上面扯了我几天的头发。一段时间以来,我们一直在使用排他性数据库锁而导致生产系统性能出现问题。我能够仔细研究一下,并注意到持有排他锁的查询是由Hibernate的延迟加载生成的选择。
我们正在使用Spring事务管理,在服务入口点定义了@Transactional( readOnly= "true")
。我们将每个请求的 session 模型与映射为传输对象的实体一起使用。数据库默认隔离级别为读取已提交。 JDBC驱动程序配置为已提交读。我已经使用以下方法检查了所涉及的实际交易的隔离级别:
select current_setting('transaction_isolation')
哪个返回读已提交。我们正在使用JPA配置Hibernate映射。我们在任何地方都没有明确升级交易。在此特定事务中,我们仅运行select语句。打开Hibernate SQL日志记录,我看不到以下任何一个:
select ... for update
仅记录简单的select语句。
似乎这里发生了两件事之一。我对读提交的理解完全不可用,读提交隔离级别应该导致在执行选择的事务期间保持排他行级锁。或发生其他事情并错误地升级了事务持有的锁。
任何帮助,将不胜感激。
编辑1 :
好的,这是一条漫长的曲折道路。事实证明,这与锁定完全无关。我用于检测锁的查询已过时,并且显示锁类型为“virtualxid”。一些挖掘告诉我们,virtualxid是每个事务对其自身都采取的锁定,因为PostgreSQL内部原因与该讨论无关。我们将另一监视查询测试称为真正的互斥锁,但还没有看到。
这是我们用于监视“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
一个简单的select current_timestamp运行一个小时以上!!!
无论如何,对于那些感兴趣的人来说,这些神秘的长期运行的查询似乎偶尔会耗尽我们的数据库连接池。因此,我们超出了连接池的限制,实时站点又恢复了嗡嗡声。我们在关键流程上设置了应用程序端超时和重试逻辑,以应对偶发的打ic。这些天,我们通常至少有一个数据库线程被困在为这些奇怪执行的查询之一提供服务。绝对不理想:(
我们将尝试启用基于成本的自动吸尘器,看看这是否可以解决所有问题。
编辑2 :
事实证明,这是一段漫长的旅程,可能就此结束。为了响应此行为,除了上面放置的数据库查询监视之外,我们还提供了批处理错误报告。再加上一些智能超时,这使我们能够将特定的应用程序用例与长期运行的数据库查询相关联。这使我们能够对生产中出现的错误使用react,以防止特定用途挂起JVM节点。
我们还能够解决以下问题:为什么一个进程中长时间运行的只读TX会挂断连接到同一数据库的其他进程。这就是事情变得有些奇怪的地方。我们正在使用hibernate-memcached将hibernate的二级缓存移到共享的memcached服务器中,以供所有连接到同一数据库的Java进程使用。每当我们遇到奇怪的挂起行为时,JVM进程中就会有大量的内存缓存客户端线程。
删除hibernate-memcached模块后,移回ehcache进行二级缓存,我们注意到奇怪的多JVM破坏性挂起消失了。我们仍然偶尔会收到一些电子邮件,告诉我们TX内部发生的事情比应该发生的要多。我们仍然偶尔会挂起单个JVM进程,因为它有太多这样的长TX大规模进行。但是我们不再看到一个JVM中的进程以某种方式影响其他JVM。以前,我们会看到其他节点无响应,直到我们杀死显示不良TX行为的初始节点为止。
这没有道理。但是后来这个问题没做过:)
-蒂姆
最佳答案
首先,您需要的是Jim Mlodgenski和Bruce Momjian在JBoss World 2009上发表的出色的扩展Hibernate应用程序和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);
关于java - Hibernate + PostgreSQL : Lazy Loading with Exclusive Locks,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15582159/