【问题标题】:Checking if the JDBC connection is writable; using preferredTestQuery or ConnectionTester检查 JDBC 连接是否可写;使用 preferredTestQuery 或 ConnectionTester
【发布时间】:2020-12-08 22:07:44
【问题描述】:

当前设置:

服务主机 (Java) 连接到 (JDBC) 主数据库 (MySQL) 并具有从属(只读)以提高可靠性

场景:

如果发生翻转,我需要将slave升级为新的master。当前 master 变为只读,新 master 提升为读写。我希望使用测试查询(对数据库执行可写查询)自动将 C3P0 连接池刷新到新的主服务器。

想法:

探索一种自动刷新 JDBC 连接池的方法,以便它们在发生翻转时连接到新的 master(当前 master 将是 RO,slave 将被提升为 RW 并且 master cname 将被更新)

当前配置

        <property name="driverClass" value="${DriverClass}" />
        <property name="jdbcUrl" value="${ReadWriteDatabaseURL}${AccountDatabaseName}${JDBCProperties}" />
        <property name="user" value="${ReadWriteDatabaseCredentials}.principal" />
        <property name="password" value="${ReadWriteDatabaseCredentials}.credential" />
        <property name="testConnectionOnCheckout" value="true"/>
        <property name="testConnectionOnCheckin" value="false" />
        <property name="preferredTestQuery" value="update existing_table set value = now() where id = 1;"/>
        <property name="maxIdleTime" value="44000"/>
        <property name="idleConnectionTestPeriod" value="30"/>
        <property name="maxStatements" value="50"/>
        <property name="minPoolSize" value="3"/>
        <property name="maxPoolSize" value="3"/>
        <property name="acquireIncrement" value="2"/>
        <property name="checkoutTimeout" value="15000"/>
        <property name="acquireRetryDelay" value="1000"/>

preferredTestQuery 作为select 1 from existing_table where 1 = 0 绝对可以正常工作

方法一:

在 C3P0 配置或休眠配置中 - 使用 preferredTestQuery 作为可写查询(类似于插入/更新现有表)并在每次连接签出时执行查询(testConnectionCheckout = true)。 不确定这是否应该是一个简单的查询来测试 db 是否已启动,因为

  • 插入/更新查询不起作用。可能我们需要begin, commit transaction 吗?
    • 错误:org.hibernate.engine.jdbc.spi.SqlExceptionHelper: An attempt by a client to checkout a Connection has timed out.
  • 多个查询不起作用。类似create table if not exists; insert into .. ; drop table ..;
    • 错误:org.hibernate.engine.jdbc.spi.SqlExceptionHelper: An attempt by a client to checkout a Connection has timed out.

方法 2: https://www.mchange.com/projects/c3p0/#configuring_connection_testing 指的是 高级用户可以定义他们希望的任何类型的连接测试,方法是实现 ConnectionTester 并提供类的完全限定名称作为 connectionTesterClassName。 我不知道怎么做,也不知道怎么做如果它适用于可写调用

上述错误的堆栈跟踪:

Caused by: org.hibernate.exception.GenericJDBCException: Could not open connection
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:221) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:157) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.internal.SessionImpl.connection(SessionImpl.java:550) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:450) ~[spring-orm-4.3.20.RELEASE.jar:4.3.20.RELEASE]
    ... 42 more
Caused by: java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
    at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118) ~[mchange-commons-java-0.2.10.jar:0.2.10]
    at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:77) ~[mchange-commons-java-0.2.10.jar:0.2.10]
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:690) ~[c3p0-0.9.5.1.jar:0.9.5.1]
    at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140) ~[c3p0-0.9.5.1.jar:0.9.5.1]
    at org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.getConnection(AbstractRoutingDataSource.java:164) ~[spring-jdbc-4.3.20.RELEASE.jar:4.3.20.RELEASE]
    at org.hibernate.service.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:141) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:292) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:214) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:157) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.internal.SessionImpl.connection(SessionImpl.java:550) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:450) ~[spring-orm-4.3.20.RELEASE.jar:4.3.20.RELEASE]
    ... 42 more
Caused by: com.mchange.v2.resourcepool.TimeoutException: A client timed out while waiting to acquire a resource from com.mchange.v2.resourcepool.BasicResourcePool@4c86da0c -- timeout at awaitAvailable()
    at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1467) ~[c3p0-0.9.5.1.jar:0.9.5.1]
    at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:644) ~[c3p0-0.9.5.1.jar:0.9.5.1]
    at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:554) ~[c3p0-0.9.5.1.jar:0.9.5.1]
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:758) ~[c3p0-0.9.5.1.jar:0.9.5.1]
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:685) ~[c3p0-0.9.5.1.jar:0.9.5.1]
    at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140) ~[c3p0-0.9.5.1.jar:0.9.5.1]
    at org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.getConnection(AbstractRoutingDataSource.java:164) ~[spring-jdbc-4.3.20.RELEASE.jar:4.3.20.RELEASE]
    at org.hibernate.service.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:141) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:292) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:214) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:157) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.hibernate.internal.SessionImpl.connection(SessionImpl.java:550) ~[hibernate-core-4.2.2.Final.jar:4.2.2.Final]
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:450) ~[spring-orm-4.3.20.RELEASE.jar:4.3.20.RELEASE]
    ... 42 more

谢谢

【问题讨论】:

    标签: mysql jdbc c3p0


    【解决方案1】:

    我无法对更大的故障转移问题发表评论,但您当然可以定义一个ConnectionTester,它基本上可以满足您的任何需求。 有关简明说明,请参阅apidocs for the UnifiedConnectionTester interface

    c3p0 的内置 ConnectionTester 使用 Statement.executeQuery(...),因此 INSERTs 等可能无法正常工作。您的自定义 ConnectionTester 可以为所欲为。如果将preferredTestQueryrootCauseOutParamHolder 设置为null(在任何一种方法中),您应该确定它会做一些合理的事情。

    如果您希望可以忽略preferredTestQuery,或者要求它为空(即,如果设置了preferredTestQuery,则抛出Exception)。

    【讨论】:

    • 是的,这正是问题所在。感谢您的帮助史蒂夫,谢谢。我指的是你的other answer over stackoverflow。并将日志级别设置为com.mchange.v2.resourcepool.BasicResourcePool 进行调试发现java.sql.SQLException: Can not issue data manipulation statements with executeQuery().
    • 但是,我想知道它是如何与testConnectionOnCheckin 而不是testConnectionOnCheckout 一起工作的但是,如果我使用testConnectionOnCheckin,则不会更新表值。我想知道 testConnectionOnCheckin 是否使用 executeUpdate() ?或者如果它忽略错误,则不确定如何将数据连接重置为新的主节点。
    • 测试是相同的,无论是在签入还是结账时。但是,签入失败虽然您应该能够将它们写入日志,但在应用程序行为方面将不那么明显。
    • 如果您使用executeQuery 拒绝的“查询”,则在结帐时进行测试时,您将始终无法获得连接。签入测试是异步的。您的应用程序不会发现问题,但在幕后 c3p0 将始终销毁和重新获取而不是重用签入的连接(看起来已损坏)。
    • 就故障转移而言,这可能看起来很棒,因为重新获取可能总是来自最新的 master。但就性能而言,它不会很好。它仍然比简单的 Connection 获取要好,因为 c3p0 将异步批量获取。但是您将通过 Connections 搅动,无论开销如何。
    猜你喜欢
    • 1970-01-01
    • 2012-10-29
    • 1970-01-01
    • 2011-05-31
    • 1970-01-01
    • 2015-08-27
    • 2020-02-22
    • 1970-01-01
    • 2014-11-22
    相关资源
    最近更新 更多