【问题标题】:'idle in transaction' when using Hibernate, Postgres and Guice Provider使用 Hibernate、Postgres 和 Guice Provider 时出现“事务中的空闲”
【发布时间】:2017-10-03 20:35:06
【问题描述】:

当我执行时:

select * from pg_stat_activity where state ~ 'idle in transact'

我得到不适当的行数,状态为“事务中的空闲”。他们中的一些人闲置了几天。其中大多数是从一个服务类(Hibernate 5.1.0.Final,Guice 4.1.0)执行的相同简单的select 查询:

public class FirebaseServiceImpl implements FirebaseService {

    @Inject
    private Provider<FirebaseKeyDAO> firebaseKeyDAO;

    @Override
    public void sendNotification(User recipient) {

        List<FirebaseKey> firebaseKeys = firebaseKeyDAO.get().findByUserId(recipient.getId());

        final ExecutorService notificationsPool = Executors.newFixedThreadPool(3);

        for (FirebaseKey firebaseKey : firebaseKeys)
            notificationsPool.execute(new Runnable() {

                 @Override
                 public void run() {
                    sendNotification(new FirebaseNotification(firebaseKey.getFirebaseKey(), "example");
                 }
        });

        notificationsPool.shutdown();
    }
}

DAO 方法:

@Override
@SuppressWarnings("unchecked")
public List<FirebaseKey> findByUserId(Long userId) {
    Criteria criteria = getSession().createCriteria(type);
    criteria.add(Restrictions.eq("userId", userId));
    return criteria.list();
}

为什么会这样?如何避免这种情况?

更新

当我在单独的线程中使用 Guice Provider exampleDAO.get() 时,未提交事务:

@Inject
Provider<ExampleDAO> exampleDAO;

【问题讨论】:

    标签: java postgresql hibernate transactions guice


    【解决方案1】:

    当您使用 pgbouncer 或其他使用 pool_mode = transaction 的池程序/会话管理器时,通常会发生这种情况。例如,当客户端打开一个事务并持有它,不提交也不回滚。检查您是否在查询列中看到 DISCARD ALL - 如果出现这种情况,because pooler 必须丢弃共享会话计划、序列、解除分配语句等,以避免在池中混合用于不同会话的那些。

    另一方面,任何“正常”事务都会给出相同的idle in transaction,例如:

    2>select now(),pg_backend_pid();
                   now                | pg_backend_pid
    ----------------------------------+----------------
     2017-05-05 16:53:01.867444+05:30 |          26500
    (1 row)
    

    如果我们检查它的状态,我们会看到正统的idle:

    t=# select query,state from pg_stat_activity where pid = 26500;
                 query              | state
    --------------------------------+-------
     select now(),pg_backend_pid(); | idle
    (1 row)
    

    现在我们在session 2 &gt; 上开始交易: 2>开始; 开始

    2>select now(),pg_backend_pid();
                   now                | pg_backend_pid
    ----------------------------------+----------------
     2017-05-05 16:54:15.856306+05:30 |          26500
    (1 row)
    

    并检查pg_stat_statements 增益:

    t=# select query,state from pg_stat_activity where pid = 26500;
                 query              |        state
    --------------------------------+---------------------
     select now(),pg_backend_pid(); | idle in transaction
    (1 row)
    

    它将一直保持这种状态,直到语句超时或事务结束:

    2>end;
    COMMIT
    t=# select query,state from pg_stat_activity where pid = 26500;
     query | state
    -------+-------
     end;  | idle
    (1 row)
    

    所以它是很常见的,可以拥有它。如果要避免连接会话,则必须断开客户端。但是 postgres 中的连接是昂贵的,所以通常人们会尝试用 pool 重用现有的连接,所以这样的状态出现在pg_stat_activity

    【讨论】:

    • 有时我有很多长时间运行的查询 'COMMIT' 状态为 'idle'。为什么会这样?是否值得像dba.stackexchange.com/questions/32890/… 中提到的那样执行'echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled'?
    • idle 表示连接在提交后仍然无所事事。查询更改时查询列更改 - 因此显示最后执行的查询...
    • 如果应用程序通过空闲提交达到 hibernate.c3p0.max_size 限制,那么它将停止使用 DB。
    • 我不知道hibernate.c3p0.max_size 是什么?...如果您获得最大连接限制,postgres 不会让您创建新连接...
    • 池中的最大JDBC连接数。
    猜你喜欢
    • 1970-01-01
    • 2013-10-15
    • 2014-03-23
    • 1970-01-01
    • 1970-01-01
    • 2015-06-22
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    相关资源
    最近更新 更多