文档章节

Oracle JDBC驱动内存

O
 Only_小白
发布于 2017/09/08 09:15
字数 2014
阅读 4
收藏 0

一、JDBC驱动内存机制

       Oracle为了提高数据库访问性能,提供了服务器端缓存和客户端缓存这两种缓存。其中,Oracle客户端缓存可能会使用大量的内存,这是一种有意识的设计选择,在使用大量内存与提高性能之间做出权衡,用内存换取性能。

     其中主要对内存产生较大影响的缓存有以下几种:

     1.查询结果缓存

     JDBC驱动缓存中缓存的是sql的执行结果,这样相同sql再次执行时可以直接从缓存中获取数据,而不需要与服务器端交互,从而达到提高查询性能的作用。默认情况下,我们在使用一个数据库连接去执行查询时,JDBC驱动会为连接内的每一个Statement(Statement、PreparedStatement、CallableStatement)创建两个buffer:byte[]和char[]。其中char[]用来保存所有字符类型的行数据,如:CHAR,VARCHAR2,NCHAR等,byte[]用来保存所有的其它类型的行数据。这些buffer在在SQL被解析的时候分配,一般也就是在第一次执行该Statement的时候。Statement会持有这两个buffer,直到它被关闭。

     2.PreparedStatement缓存

     大型系统中往往很多次执行相同的sql,出于性能方面考虑,驱动重用了PreparedStatement,而不是每次为每条sql创建新的PreparedStatement。JDBC驱动内置了一个语句缓存-- Implicit Statement Cache(它的大小默认为0,即不缓存PreparedStatement)。这个语句缓存对用户是透明的,可通过配置连接属性,设置缓存的PreparedStatement数量。

     3.buffer缓存

     Oracle11.2版本驱动中,驱动程序中提供了一个内存管理更复杂的方法。这个方法有两个目标,最大限度减少内存未使用量和最小化buffer分配带来的成本。驱动程序在每个连接内部创建了一个buffer缓存(buffer cache)。当一个PreparedStatment使用结束被放回Implicit Statement Cache中时,它的buffer会被缓存到buffer缓存中buffer桶(buncket)中,一个buffer桶中的所有buffer都是相同大小且这个大小是预先确定的。当一个PreparedStatement是从Implicit Statement中取出时,也同时会从buffer缓存中根据查询结果的大小从适当的buffer桶中取出buffer。

驱动提供一个连接属性oracle.jdbc.maxCachedBufferSize。它是一个int字符串,默认是Integer.MAX_VALUE。此属性限定了保存在buffer桶中buffer最大的长度大小。超过这个大小 的buffer会在PreparedStatement被放回Implicit Statement Cache中时释放,小于这个大小的buffer会被缓存到相应的buffer桶中,当PreparedStatement从Implicit Statement Cache中取出时,小于maxCachedBufferSize的buffer会从合适的buffer 桶中取出,大于maxCachedBufferSize的buffer 会被重新创建。

    

二、内存占用情况

       buffer是在SQL解析的时候被分配的,buffer的大小并不取决于查询返回的行数据的实际长度,而是行数据可能的最大的长度。在SQL解析时,每列的类型是已知的,从该信息中驱动程序可以计算存储每一列所需的内存的最大长度。驱动程序也有fetchSize属性,也就是每次fetch返回的行数。有了每列有大小和行数的大小,驱动程序可以由此计算出一次fetch所返回的数据最大绝对长度。这也就是所分配的buffer的大小。

     字符数据存储在char[]buffer中。Java中的每个字符占用两个字节。一个VARCHAR2(10)列将包含最多10个字符,也就是10个Java的字符,也就是每行20个字节。一个VARCHAR2(4000)列将占用每行8K字节。重要的其实是column的定义大小,而不是实际数据的大小。一个VARCHAR2(4000)但是只包含了NULL的列,仍然需要每行8K字节。buffer是在驱动程序看到的查询结果之前被分配的,因此驱动程序必须分配足够的内存,以应付最大可能的行大小。一个定义为VARCHAR2(4000)的列最多可包含4000个字符。Buffer必须大到足以容纳4000个字符分配,尽管实际的结果数据可能没有那么大。

     BFILE,BLOB和CLOB会被存储为locator。Locator可高达4K字节,每个BFILE,BLOB和CLOB列的byte[]必须有至少每行4K字节。RAW列最多可以包含4K字节。其它类型的则需要很少的字节。一个合理的近似值是假设所有其它类型的列,每行占用22个字节。

      假设目前数据库中,每条记录定义:10K,fetchsize:50,PreparedStatementCache:100,连接池max值:30, 连接池个数:2个。在极端情况下,内存占用会达到:2*10K*50*100*30*2=6GB 。

三、控制参数说明

       说明以下参数都可以通过-D方式添加到启动sofa容器的启动参数中设置系统属性。

 

     1.oracle.jdbc.maxCachedBufferSize

此属性限定了保存在buffer缓存中buffer的最大的长度大小。它是一个int字符串,默认是Integer.MAX_VALUE。超过这个大小 的buffer会在PreparedStatement被放回Implicit Statement Cache中时释放,小于这个大小的buffer会被缓存到相应的buffer桶中,当PreparedStatement从Implicit Statement Cache中取出时,小于maxCachedBufferSize的buffer会从合适的buffer 桶中取出,大于maxCachedBufferSize的buffer 会被重新创建。

     2. oracle.jdbc.useThreadLocalBufferCache

默认值:false,默认情况下,buffer缓存中存储在连接中的,即每个连接都会有自己的缓存。如果在真实业务场景中,我们存在大量这样的使用场景:在一个处理逻辑中,我们需要使用多个连接去访问数据库,那么这些连接各自都有自己的缓存。且相对于线程数来说存在大量空闲数据库连接,由于默认情况下buffer缓存是附属于每个连接的,空闲连接的结果就是buffer缓存中有很多不会被用到的buffer,会用很多没必要的内存。我们可以把oracle.jdbc.useThreadLocalBufferCache属性设置为true,这样我们可以让业务处理逻辑所在线程内的所有连接共用一个缓存,即把buffer存储到线程中,如此一来,线程内使用到的连接都会共用当前线程内的buffer。该属性可以通过-D设置System property或者通过的调用getConnection时的connection property。

    3. OracleStatement.setLobPrefetchSize

              LOB字段默认大小为4000bytes。设置BLOB字段每次获取字节数据的大小,这种方式也可以限制一行查询结果的大小。因sofa底层使用的是hibernate,不支持对该参数的设置控制。在使用jdbc方式时可以设置该参数。

     4.fetchSize

              buffer的大小不是由查询的实际行数决定的,而是由每次获取的行数决定的。这个每次获取的行数也就是fetchSize,见下图。

      

JDBC驱动默认是10。我们可以在hibernate_properties_config.xml中通过配置

   <prop key="hibernate.jdbc.fetch_size">50</prop>来设置。

       或在连接池中按不同连接池的属性去配置fetchSize大小。

5.优化连接池

        buffer默认是存储在每个数据库连接中的。连接池中配置太多的连接,会导致连接大部分空闲。在极端情况下,如果连接池内的连接被全部使用,每个连接内都会有查询结果缓存,就会导致应用程序内存占用暴增。

© 著作权归作者所有

共有 人打赏支持
O
粉丝 2
博文 22
码字总数 11544
作品 0
昌平
Oracle的JDBC驱动的版本你了解吗?

classes12.jar,ojdbc14.jar,ojdbc5.jar和ojdbc6.jar的区别,之间的差异 在使用Oracle JDBC驱动时,有些问题你是不是通过替换不同版本的Oracle JDBC驱动来解决的?最常使用的ojdbc14.jar有多...

i33
2012/02/20
0
0
Spring Boot 2.0.3 JDBC整合Oracle 12

整合步骤 1. Oracle驱动引入 Oracle驱动一般不能通过maven仓库直接下载得到,需自行下载并导入到项目的lib目录下,建议通过如下pom依赖引入下载的Oracle驱动 2. POM依赖 3. application.pro...

OSC_fly
07/24
0
0
java连接各种数据库(mysql,sql server,oracle,db2)

MYSQL: private String conUrl ="jdbc:mysql://localhost:3306/数据库"网站推广 Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(conUrl,"用户名","密码"); S......

网络营销
2012/01/09
0
0
Oracle JDBC驱动与时间不见了的问题

一般的数据库中,DATE字段仅仅表示日期,不包括日期信息,而Oracle数据库中的DATE数据类型是包括日期、时间的,对于不同的Oracle jdbc驱动版本,对于该问题的处理都有些区别,如果你使用9i或...

三毛々
2012/11/22
0
0
jmeter(八)-JDBC请求(sqlserver)

做JDBC请求,首先要了解这个JDBC对象是什么,然后寻找响应的数据库连接URL和数据库驱动。 数据库URL:jdbc:sqlserver://200.99.197.190:1433;databaseName=ebank 数据库驱动:com.microsoft...

劲风online
2015/01/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

你为什么在Redis里读到了本应过期的数据

一个事故的故事 晚上睡的正香突然被电话吵醒,对面是开发焦急的声音:我们的程序在访问redis的时候读到了本应过期的key导致整个业务逻辑出了问题,需要马上解决。 看到这里你可能会想:这是不...

IT--小哥
今天
2
0
祝大家节日快乐,阖家幸福! centos GnuTLS 漏洞

yum update -y gnutls 修复了GnuTLS 漏洞。更新到最新 gnutls.x86_64 0:2.12.23-22.el6 版本

yizhichao
昨天
5
0
Scrapy 1.5.0之选择器

构造选择器 Scrapy选择器是通过文本(Text)或 TextResponse 对象构造的 Selector 类的实例。 它根据输入类型自动选择最佳的解析规则(XML vs HTML): >>> from scrapy.selector import Sele...

Eappo_Geng
昨天
4
0
Windows下Git多账号配置,同一电脑多个ssh-key的管理

Windows下Git多账号配置,同一电脑多个ssh-key的管理   这一篇文章是对上一篇文章《Git-TortoiseGit完整配置流程》的拓展,所以需要对上一篇文章有所了解,当然直接往下看也可以,其中也有...

morpheusWB
昨天
5
0
中秋快乐!!!

HiBlock
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部