我的Java Hibernate应用程序是否使用了预处理语句池?

8
我正在使用Hibernate 4.3.11.Final和H2 1.3.172数据库,并在一个较慢的Linux机器上对我的应用程序进行分析,发现它在特定的SQL INSERT上花费了比其他任何操作都要多的时间。同时,似乎预编译语句没有被缓存,因为预编译语句的数量与执行的语句数量大致相同。我是否理解正确(我正在使用Yourkit Profiler)?

enter image description here

我的HibernateUtil类配置如下

public static Configuration getInitializedConfiguration()
    {
        Configuration config = new Configuration();

        config.setProperty(Environment.DRIVER,"org.h2.Driver");
        config.setProperty(Environment.URL,"jdbc:h2:"+Db.DBFOLDER+"/"+Db.DBNAME+";FILE_LOCK=SOCKET;MVCC=TRUE;DB_CLOSE_ON_EXIT=FALSE;CACHE_SIZE=50000");
        config.setProperty(Environment.DIALECT,"org.hibernate.dialect.H2Dialect");
        System.setProperty("h2.bindAddress", InetAddress.getLoopbackAddress().getHostAddress());
        config.setProperty("hibernate.connection.username","jaikoz");
        config.setProperty("hibernate.connection.password","jaikoz");
        config.setProperty("hibernate.c3p0.numHelperThreads","10");
        config.setProperty("hibernate.c3p0.min_size","20");
        //Consider that if we have lots of busy threads waiting on next stages could we possibly have alot of active
        //connections.
        config.setProperty("hibernate.c3p0.max_size","200");
        config.setProperty("hibernate.c3p0.timeout","300");
        config.setProperty("hibernate.c3p0.maxStatementsPerConnection","50");
        config.setProperty("hibernate.c3p0.idle_test_period","3000");
        config.setProperty("hibernate.c3p0.acquireRetryAttempts","10");
        addEntitiesToConfig(config);
        return config;
    }

我想知道是否配置不正确,令人困惑的是c3po文档与Hibernate配置在某些参数名称上并不相符,例如是“max_size”或“max_pool_size”。
这是一个单用户多线程应用程序,理想情况下,我希望所有准备好的语句都在应用程序的持续时间内被缓存,因为只有大约50个不同的语句。
据我所知,每次执行操作时,缓存都会失效。
session = HibernateUtil.beginTransaction();

这将从池中获取连接,如果该特定连接先前已准备好现在所需的语句,则可以使用该准备好的语句而无需编译新语句。

如果准备好的语句不存在,则会准备。

如果此连接已经有50个准备好的语句,则最旧的语句将被删除。

这个需要更多时间的特定查询的用法如下:

public static void saveMatchedToRelease(Session session,Integer reportId, Integer recNo, SongFieldKey songFieldKey, SongChangeType type, String original, String edited)
{
    SongChanges sc = new SongChanges();
    sc.setReportId(reportId);
    sc.setRecNo(recNo);
    sc.setField(songFieldKey);
    sc.setType(type);
    sc.setOriginalValue(original);
    sc.setNewValue(edited);
    session.save(sc);
}

只是为了确认,因为在截图中不明显 - 您是否将 hibernate-c3p0 工件作为依赖项? 在早期版本的 Hibernate 中,org.hibernate.c3p0.internal.C3P0ConnectionProvider 类是在主要的 hibernate.jar 中提供的,因此许多 Hibernate/C3P0 的说明没有提到额外的依赖项。 - df778899
@df778899 嗯,我不知道。我以为它应该是 Hibernate 的一部分,我需要显式地添加吗?我正在使用 Maven。 - Paul Taylor
我认为不会 - hibernate-c3p0 不是 hibernate-core 的传递依赖项。但是再次查看截图,它显示了来自 C3P0 的较低级别的 NewProxyConnection 等类。另一个可能性是,hibernate-core.jar 和旧版的 hibernate.jar 都在依赖树中的某个位置。如果它针对更新的 Hibernate 拾取了过时的 C3P0ConnectionProvider,则可能无法通过设置传递。 - df778899
3个回答

2
在H2中,预处理语句被缓存到连接级别。如果使用hibernate.c3p0.max_size=200,有可能会打开太多的连接,导致每次用户执行操作时都会得到一个不同的H2连接。
相比其他关系型数据库,本地内存中的H2的连接成本很小。尝试移除C3P0并使用单个H2连接进行测试。这应该能够确认JDBC驱动程序已经缓存了预处理语句。
在大多数情况下,较少的连接是更好的选择。在您的情况下,由于只有一个用户和多个线程,您的机器很可能没有200个CPU可以充分利用hibernate.c3p0.max_size=200

这是一个不错的观点,我已经大幅减少了最大连接数,但不确定如何测试是否正在重复使用预编译语句。 - Paul Taylor
我没有使用内存中的h2,而是使用基于磁盘的h2,因为我使用数据库的整个原因是为了减少内存消耗。 - Paul Taylor

0

我认为缓存不是问题所在。如果您每次插入都使用新的事务,并且始终使用不同的线程插入到歌曲表中,那么可能会因为打开事务而阻塞自己。多线程很容易导致错误,特别是在打开数据库事务时。


0

自 JDBC 3.0 起,将 setMaxStatements(n) 设置为正数将启用语句池和缓存。

如果有人在使用 Oracle JDBC 驱动程序,则可以通过以下任一方法验证缓存:

在连接上,

if (conn.getImplicitCachingEnabled())
  System.out.println("\nimplicit caching enabled"); 
else
  System.out.println("\nimplicit caching disabled");

关于语句,

int creationState = stmt.creationState();
switch(creationState) {
case 0:
System.out.println("\nCreation state: new");
break;
case 1:
System.out.println("\nCreation state: from the implicit cache"); 
break;
case 2:
System.out.println("\nCreation state: from the explicit cache"); 
break;
}

参考: Oracle语句池和缓存

问题是关于H2数据库,而不是Oracle。 - Karol Dowbecki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接