我正在努力理解数据源如何运行验证。我有一个连接池,可以在 Firebird 数据库上运行查询,但池中的连接很少,会引发错误,例如
Insufficient memory to allocate page buffer cache [SQLState:HY013, ISC error code:335544691
我不确定为什么数据源不使用验证查询使它们无效。我创建的数据源不是容器托管的,因此不确定这是否是未调用验证查询的原因。
我创建了一个数据源 bean 并将其注册到春豆上,如下所示。
builder.beans {
"${beanName}"(org.apache.tomcat.jdbc.pool.DataSource) {
driverClassName = "${configuration.driver}"
url = configuration.connectionUrlPrefix
username = configuration.userName
password = configuration.password
maxActive = properties.maxActive
maxIdle = properties.maxIdle
minIdle = properties.minIdle
initialSize = properties.initialSize
maxWait = properties.maxWait
validationQuery = properties.validationQuery
validationInterval = properties.validationInterval
testWhileIdle = properties.testWhileIdle
testOnBorrow = properties.testOnBorrow
logAbandoned = properties.logAbandoned
removeAbandoned = properties.removeAbandoned
removeAbandonedTimeout = properties.removeAbandonedTimeout
timeBetweenEvictionRunsMillis = properties.timeBetweenEvictionRunsMillis
minEvictableIdleTimeMillis = properties.minEvictableIdleTimeMillis
}
}
堆栈跟踪:
2018-05-02 11:30:52,766 [ajp-bio-8012-exec-7] ERROR StackTrace - Full Stack Trace:
java.sql.SQLException: Insufficient memory to allocate page buffer cache [SQLState:HY013, ISC error code:335544691]
at org.firebirdsql.gds.ng.FbExceptionBuilder$Type$1.createSQLException(FbExceptionBuilder.java:498)
at org.firebirdsql.gds.ng.FbExceptionBuilder.toFlatSQLException(FbExceptionBuilder.java:299)
at org.firebirdsql.gds.ng.wire.AbstractWireOperations.readStatusVector(AbstractWireOperations.java:135)
at org.firebirdsql.gds.ng.wire.AbstractWireOperations.processOperation(AbstractWireOperations.java:199)
at org.firebirdsql.gds.ng.wire.AbstractWireOperations.readSingleResponse(AbstractWireOperations.java:166)
at org.firebirdsql.gds.ng.wire.AbstractWireOperations.readResponse(AbstractWireOperations.java:150)
at org.firebirdsql.gds.ng.wire.AbstractWireOperations.readGenericResponse(AbstractWireOperations.java:252)
at org.firebirdsql.gds.ng.wire.version10.V10WireOperations.authReceiveResponse(V10WireOperations.java:52)
at org.firebirdsql.gds.ng.wire.version10.V10Database.authReceiveResponse(V10Database.java:566)
at org.firebirdsql.gds.ng.wire.version10.V10Database.attachOrCreate(V10Database.java:110)
at org.firebirdsql.gds.ng.wire.version10.V10Database.attach(V10Database.java:80)
at org.firebirdsql.jca.FBManagedConnection.<init>(FBManagedConnection.java:144)
at org.firebirdsql.jca.FBManagedConnectionFactory.createManagedConnection(FBManagedConnectionFactory.java:520)
at org.firebirdsql.jca.FBStandAloneConnectionManager.allocateConnection(FBStandAloneConnectionManager.java:65)
at org.firebirdsql.jdbc.FBDataSource.getConnection(FBDataSource.java:117)
at org.firebirdsql.jdbc.FBDriver.connect(FBDriver.java:137)
at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:278)
at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:182)
at org.apache.tomcat.jdbc.pool.ConnectionPool.createConnection(ConnectionPool.java:712)
at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:646)
at org.apache.tomcat.jdbc.pool.ConnectionPool.init(ConnectionPool.java:468)
at org.apache.tomcat.jdbc.pool.ConnectionPool.<init>(ConnectionPool.java:145)
at org.apache.tomcat.jdbc.pool.DataSourceProxy.pCreatePool(DataSourceProxy.java:116)
at org.apache.tomcat.jdbc.pool.DataSourceProxy.createPool(DataSourceProxy.java:103)
at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:127)
at javax.sql.DataSource$getConnection.call(Unknown Source)
at
该错误本身意味着 Firebird 尝试为页面缓冲区缓存分配内存,但无法分配(因为操作系统内存不足,或者它达到了进程可用的最大内存量(。该问题与验证查询无关,堆栈跟踪显示它在创建新连接时发生。如果池无法分配新连接来为您的请求提供服务,它将放弃。
根据我们在评论中的交流,看起来这个Firebird服务器是在经典服务器(CS(或超级经典(SC(模式下配置的。在此模式下,页面缓冲区缓存是按连接而不是按数据库(在超级服务器 (SS( 模式下是按数据库缓存(。因此,连接越多,内存消耗就越高。
这表明您分配的连接过多,或者配置的缓存页数过高(最具体的配置 - 如果设置了 - 适用(:
firebird.conf
设置DefaultDbCachePages
( 设置得太高(CS/SC 的默认值为 75,SS 的默认值为 2048(- 特定于数据库的设置(请参阅
gstat -h
输出( - 连接属性
isc_dpb_num_buffers
/num_buffers
对于经典/超级经典,这会导致为每个连接分配NCachePages *页面大小字节的内存,其中页面大小是数据库的页面大小(通常为 8 或 16 KB(。
例如,对于页面大小为 16kb 的 CS/SC,默认为 75 个缓冲区时,每个连接占用 1228800 (1.2 MB( 的内存作为缓存(,因此 100 个连接需要 122 MB(忽略缓存之外的其他内存需求(。
另一方面,如果此设置已更改(全局、数据库或每个连接设置(更改为例如 9999 页,则每个连接占用 163 MB 内存,100 个连接需要 16GB。
若要解决此问题,需要执行以下一个或多个步骤:
- 减少所需的连接量(使用良好的连接池和较小的工作单元,您可能会惊讶于所需的连接数量如此之少(
- 减少分配给缓存的页数
- 增加可用于火鸟进程的内存
- 通过使用较小的页面大小备份和还原数据库来减小页面大小
- 切换到超级服务器模式而不是经典/超级经典
最后两个可能导致性能发生重大变化(可能是积极的,可能是消极的(,因此应该仔细测试这些变化。
解决方法是,如果无法让所有者在短时间内更改配置,请考虑将连接属性num_buffers=75
(或类似的低数字(添加到连接属性中。