连接到 Firebird 数据库时出错 "Insufficient memory to allocate page buffer cache"
Error "Insufficient memory to allocate page buffer cache" while connecting to Firebird database
我很难理解数据源如何运行验证。我有一个在 Firebird 数据库上运行查询的连接池,但是池中的连接很少会抛出
之类的错误
Insufficient memory to allocate page buffer cache [SQLState:HY013, ISC error code:335544691
我不确定为什么数据源不使用验证查询使它们无效。我创建的数据源不是容器管理的,所以不确定这是否是未调用验证查询的原因。
我创建了一个数据源 bean 并将其注册到 spring 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.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 试图为页面缓冲区缓存分配内存,但不能(因为 OS 运行 内存不足,或者它达到了最大内存量进程可用)。该问题与验证查询无关,堆栈跟踪显示它是在创建新连接时发生的。如果池无法分配新连接来满足您的请求,它将放弃。
根据我们在评论中的交流,这台 Firebird 服务器似乎配置为经典服务器 (CS) 或超经典 (SC) 模式。在这种模式下,页面缓冲区缓存是针对每个连接的,而不是针对每个数据库的(在 SuperServer (SS) 模式中,它是针对每个数据库的)。结果连接越多,内存消耗就越大。
这表明您分配的连接太多,或者配置的缓存页数太高(最具体的配置 - 如果设置 - 适用):
firebird.conf
设置 DefaultDbCachePages
) 设置得太高(CS/SC 的默认值为 75,SS 的默认值为 2048)
- 特定于数据库的设置(参见
gstat -h
输出)
- 连接属性
isc_dpb_num_buffers
/num_buffers
使用 Classic/SuperClassic 这导致分配 NCachePages * Pagesize 字节内存 每个连接,其中 Pagesize 是数据库的页面大小(通常为 8 或 16 KB)。
例如,对于页面大小为 16kb 的 CS/SC,默认情况下有 75 个缓冲区,每个连接需要 1228800(1.2 MB)内存用于缓存),因此 100 个连接需要 122 MB(忽略其他)缓存之外的内存需求)。
另一方面,如果此设置已更改(全局、数据库或每个连接设置),例如,9999 页,则每个连接占用 163 MB 内存,而 100 个连接将需要 16GB。
要解决此问题,您需要执行以下一个或多个步骤:
- 减少所需的连接数量(使用良好的连接池和小的工作单元,您可能会惊讶于您需要的连接数量如此之少)
- 减少分配给缓存的页面数量
- 增加 Firebird 进程可用的内存
- 通过使用较小的页面大小备份和还原数据库来减少页面大小
- 切换到 SuperServer 模式而不是 Classic/SuperClassic
最后两个可能会导致性能发生重大变化(可能是积极的,也可能是消极的),因此应仔细测试。
作为解决方法,如果您不能让所有者在短时间内更改配置,请考虑将连接 属性 num_buffers=75
(或类似的低数字)添加到您的连接属性.
我很难理解数据源如何运行验证。我有一个在 Firebird 数据库上运行查询的连接池,但是池中的连接很少会抛出
之类的错误Insufficient memory to allocate page buffer cache [SQLState:HY013, ISC error code:335544691
我不确定为什么数据源不使用验证查询使它们无效。我创建的数据源不是容器管理的,所以不确定这是否是未调用验证查询的原因。
我创建了一个数据源 bean 并将其注册到 spring 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.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 试图为页面缓冲区缓存分配内存,但不能(因为 OS 运行 内存不足,或者它达到了最大内存量进程可用)。该问题与验证查询无关,堆栈跟踪显示它是在创建新连接时发生的。如果池无法分配新连接来满足您的请求,它将放弃。
根据我们在评论中的交流,这台 Firebird 服务器似乎配置为经典服务器 (CS) 或超经典 (SC) 模式。在这种模式下,页面缓冲区缓存是针对每个连接的,而不是针对每个数据库的(在 SuperServer (SS) 模式中,它是针对每个数据库的)。结果连接越多,内存消耗就越大。
这表明您分配的连接太多,或者配置的缓存页数太高(最具体的配置 - 如果设置 - 适用):
firebird.conf
设置DefaultDbCachePages
) 设置得太高(CS/SC 的默认值为 75,SS 的默认值为 2048)- 特定于数据库的设置(参见
gstat -h
输出) - 连接属性
isc_dpb_num_buffers
/num_buffers
使用 Classic/SuperClassic 这导致分配 NCachePages * Pagesize 字节内存 每个连接,其中 Pagesize 是数据库的页面大小(通常为 8 或 16 KB)。
例如,对于页面大小为 16kb 的 CS/SC,默认情况下有 75 个缓冲区,每个连接需要 1228800(1.2 MB)内存用于缓存),因此 100 个连接需要 122 MB(忽略其他)缓存之外的内存需求)。
另一方面,如果此设置已更改(全局、数据库或每个连接设置),例如,9999 页,则每个连接占用 163 MB 内存,而 100 个连接将需要 16GB。
要解决此问题,您需要执行以下一个或多个步骤:
- 减少所需的连接数量(使用良好的连接池和小的工作单元,您可能会惊讶于您需要的连接数量如此之少)
- 减少分配给缓存的页面数量
- 增加 Firebird 进程可用的内存
- 通过使用较小的页面大小备份和还原数据库来减少页面大小
- 切换到 SuperServer 模式而不是 Classic/SuperClassic
最后两个可能会导致性能发生重大变化(可能是积极的,也可能是消极的),因此应仔细测试。
作为解决方法,如果您不能让所有者在短时间内更改配置,请考虑将连接 属性 num_buffers=75
(或类似的低数字)添加到您的连接属性.