连接到 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(或类似的低数字)添加到您的连接属性.