Oracle数据库下PreparedStatementCache内存问题解决方案

PreparedStatementCache的意义

Oracle支持游标,一个PreparedStatement对应服务器一个游标,如果PreparedStatement被缓存起来重复执 行,PreparedStatement没有被关闭,服务器端的游标就不会被关闭,性能提高非常显著。在类似SELECT * FROM T WHERE ID = ?这样的场景,性能可能是一个数量级的提升。

由于PreparedStatementCache性能提升明显,DruidDataSource、DBCP、JBossDataSource、WeblogicDataSource都实现了PreparedStatementCache。

PreparedStatementCache带来的问题

阿里巴巴在使用jboss连接池做PreparedStatementCache时,遇到了full gc频繁的问题。通过mat来分析jmap dump的结果,发现T4CPreparedStatement占内存很多,出问题的几个项目,有的300M,有的500M,最夸张的900M。这些应用 都是用jboss连接池访问Oracle数据库,T4CPreparedStatement是Oracle JDBC Driver的PreparedStatement一种实现。

oracle driver不是开源,通过逆向工程以及mat分析,发现其中占内存的是字段char[] defineChars,defineChars大小的计算公式是这样的:

defineChars大小 = rowSize * rowPrefetchCount

rowPrefetchCount在Oracle中,缺省值为10。

其中rowSize是执行查询设计的每一列的大小的和。计算公式是:

rowSize = col_1_size + col_2_size + ... + col_n_size

很悲剧,有些列数据类型是varchar2(4000),于是rowSize巨大,很多个表关联的SQL,rowSize可能高达数十K,再乘以 rowPrefetchCount,defineChars大小接近1M。可以想想,maxPoolSize设置为 30,PreparedStatementCacheSize设置为50的场景下,是可能导致PreparedStatementCache占据上G的内 存。
实际测试得到的结果如下:

varchar2(4000) col_size 4000 chars
clob -> col_size col_size 4000 bytes

实际占据内存的公式:

占据内存大小峰值 = defineChars大小 * PreparedStatementCacheSize * MaxPoolSize

我们实际分析,一个应用运行的SQL大约数百条,PreparedStatementCacheSize为 50,PreparedStatementCache的算法为LRU,很多的SQL执行之后,在Cache中HitCount为0就被淘汰了,淘汰的过 程,其位置从第1移到第50,这个漫长的过程导致了defineChars不能够被young gc回收。

Druid的解决方案

命中率低的SQL,不持有defineChars缓存。

OracleStatement提供了clearDefines方法,用于清理defineChars。DruidDataSource在 PreparedStatement加入Cache时,如果发现hitCount < 1,就调用clearDefines方法。通过这种方式,避免了命中率为0的PreparedStatement占用内存。测试表明,200个 SQL,PreparedStatementCacheSize为50的场景,能够减少一半的young gc。(生产环境还没做测试)

根据PreparedStatement执行的结果,计算RowPrefetch大小

添加PreparedStatementCache计数器

包括:

  • PreparedStatementCacheCurrentSize
  • PreparedStatementCacheDeleteCount 缓存删除次数
  • PreparedStatementCacheHitCount 缓存命中次数
  • PreparedStatementCacheMissCount 缓存不命中次数
  • PreparedStatementCacheAccessCount 缓存访问次数
    通过这五个计数器,我们清晰了解PreparedStatementCache的工作情况,然后根据实际情况调整。

猜你喜欢

转载自zl198751.iteye.com/blog/1331470