JDBC性能技巧
翻译:陈先波(turbochen@163.com)
日期:2004/6/10
阅读原文:http://www.theserverside.com/articles/article.tss?l=JDBCPerformance
April 2004
介绍
John Goodson 是一名JDBC专家组成员,也是DataDirect Technologies公司的副总裁.
开发一个注重性能的JDBC应用程序不是一件容易的事. 当你的代码运行很慢的时候JDBC驱动程序并不会抛出异常告诉你.
本系列的性能提示将为改善JDBC应用程序的性能介绍一些基本的指导原则,这其中的原则已经被许多现有的JDBC应用程序编译运行并验证过。 这些指导原则包括:
正确的使用数据库MetaData方法
只获取需要的数据
选用最佳性能的功能
管理连接和更新
以下这些一般性原则可以帮助你解决一些公共的JDBC系统的性能问题.
使用数据库Metadata方法
因为通过ResultSet对象生成的Metadata方法与其它的JDBCB方法相比是较慢的, 经常的使用它们将会削弱系统的的性能. 本节的指导原则将帮助你选择和使用meatdata时优化系统性能.
少用Metadata方法
与其它的JDBC方法相比, 由ResultSet对象生成的metadata对象的相对来说是很慢的. 应用程序应该缓存从ResultSet返回的metadata信息,避免多次不必要的执行这个操作.
几乎没有哪一个JDBC应用程序不用到metadata,虽然如此,你仍可以通过少用它们来改善系统性能. 要返回JDBC规范规定的结果集的所有列信息, 一个简单的metadata的方法调用可能会使JDBC驱动程序去执行很复杂的查询甚至多次查询去取得这些数据. 这些细节上的SQL语言的操作是非常消耗性能的.
应用程序应该缓存这些metadata信息. 例如, 程序调用一次getTypeInfo方法后就将这些程序所依赖的结果信息缓存. 而任何程序都不大可能用到这些结果信息中的所有内容,所以这些缓存信息应该是不难维护的.
避免null参数
在metadata的方法中使用null参数或search patterns是很耗时的. 另外, 额外的查询会导致潜在的网络交通的增加. 应尽可能的提供一些non-null的参数给metadata方法.
因为metadata的方法很慢, 应用程序要尽可能有效的调用它们. 许多应用程序只传递少量的non-null参数给这些方法.
例如:
ResultSet WSrs = WSc.getTables (null, null, "WSTable", null);
应该这样:
ResultSet WSrs = WSc.getTables ("cat1", "johng", "WSTable", "TABLE");
在第一个getTables()的调用中, 程序可能想知道表'WSTable'是否存在. 当然, JDBC驱动程序会逐个调用它们并且会解译不同的请求. JDBC驱动程序会解译请求为: 返回所有的表, 视图, 系统表, synonyms, 临时表, 或存在于任何数据库类别任何Schema中的任何别名为'WSTable'的对象.
第二个getTables()的调用会得到更正确的程序想知道的内容. JDBC驱动程序会解译这个请求为: 返回当前数据库类别中所有存在于'johng'这个schema中的所有表.
很显然, JDBC驱动程序处理第二个请求比处理第一个请求更有效率一些.
有时, 你所请求信息中的对象有些信息是已知的. 当调用metadata方法时, 程序能传送到驱动程序的的任何有用信息都可以导致性能和可靠性的改善.
使用'哑元'(dummy)查询确定表的特性
要避免使用getColumns()去确定一个表的特性. 而应该使用一个‘哑元’查询来使用getMetadata()方法.
请考虑这样一个程序, 程序中要允许用户选取一些列. 我们是否应该使用getColumns()去返回列信息给用户还是以一个'哑元'查询来调用getMetadata()方法呢?
案例 1: GetColumns 方法
ResultSet WSrc = WSc.getColumns (... "UnknownTable" ...);
// getColumns()会发出一个查询给数据库系统
. . .
WSrc.next();
string Cname = getString(4);
. . .
// 用户必须从反复从服务器获取N行数据
// N = UnknownTable的列数
案例 2: GetMetadata 方法
// 准备'哑元'查询
PreparedStatement WSps = WSc.prepareStatement
("SELECT * from UnknownTable WHERE 1 = 0");
// 查询从来没有被执行,只是被预储
ResultSetMetaData WSsmd=WSps.getMetaData();
int numcols = WSrsmd.getColumnCount();
...
int ctype = WSrsmd.getColumnType(n)
...
// 获得了列的完整信息
在这两个案例中, 一个查询被传送到服务器. 但在案例1中, 查询必须被预储和执行, 结果的描述信息必须确定(以传给getColumns()方法), 并且客户端必须接收一个包含列信息的结果集. 在案例2中, 只要准备一个简单的查询并且只用确定结果描述信息. 很显然, 案例2执行方式更好一些.
这个讨论有点复杂, 让我们考虑一个没有本地化支持prepared statement的DBMS服务器. 案例1的性能没有改变, 但案例2中, 因为'哑元'查询必须被执行而不是被预储使得它的性能增强了一些. 因为查询中的WHERE子句总是为FALSE, 查询在不用存取表的数据情况的下会生成没有数据的结果集. 在这种情况下,第二种方式当然比第一种方式好一些.
总而言之,总是使用ResultSet的metadata方法去获取列信息,像列名,列的数据类型,列的数据精度和长度等. 当要求的信息无法从ResultSet的metadata中获取时才去用getColumns()方法(像列的缺省值这些信息等).
下一个月的性能提示是 '只获取需要的数据'.