C. M. Saracco,IBM 硅谷实验室, 加利福尼亚圣何塞
Susanne Englert, 加利福尼亚圣何塞, IBM 硅谷实验室
Ingmar Gebert, 加利福尼亚圣何塞, IBM 硅谷实验室
2003 年 5 月
J2EE 开发人员们,请注意了!通过使用业务合并方案,我们的几位作者自己体验到了使用 DB2 Information Integrator 访问异构后端数据源(包括 Oracle、DB2 UDB 和 Microsoft Excel)时,他们的代码是多么更简单。这是由三部分组成的系列文章的第一部分。
简介
您听过这样一个故事:还有另一种新技术可望使 Java 2 企业版(Java 2 Enterprise Edition,J2EE)开发人员更轻松地工作 - 这个突破可以使开发工作减少 30%、40% 甚至 50%。提供该技术的新软件简化了复杂任务。提供该技术的新产品增强了您现有 Java™ 集成开发平台和 Web 应用程序服务器环境。
我们也听过这个故事。我们是在同事们讨论 IBM 新的 DB2® Information Integrator 时听到的,这个产品旨在提供数据的单点映像,这些数据驻留在不同平台上、以不同格式存储并可通过不同的应用程序编程接口(API)来访问。而我们只想知道:对于承担构建处理异构数据所需的 Web 组件这个任务的 J2EE 程序员而言,这种技术可以完成什么。
在本系列文章中,我们将讨论我们构建的一系列 servlet,它们需要访问散布于多个数据源(在我们的例子中,是 DB2、Oracle 和 Excel)上的数据。一组 servlet 使用了 DB2 Information Integrator 来模拟这些数据源的单点映像,而另一组直接访问所需的每个数据源。
其结果令我们吃惊,而我们计划和您分享这些结果。
在这个由三部分组成的系列文章中,我们将带您查看我们的工作,这样您就可以自己比较不同的实现,并了解我们必须克服的难题。本文是该系列文章的第一篇,将描述我们的项目并总结我们的重大发现。第二篇文章将集中讨论我们的开发工作,带您更仔细地查看设计和编码问题。最后一篇文章将描述我们遇到的性能问题,并提供我们的联邦及本机数据访问实现的一些性能数据。
但如果您已经迫不及待地想要了解最终结果,那么结果就是:我们发现,当我们使用 DB2 Information Integrator 中的联邦数据库技术时,我们的开发工作比直接使用每个数据源的确轻松得多 - 而且快得多。特别是,我们削减了大约 40% 的 servlet 代码,而且缩短了超过 50% 的设计、开发和测试周期。这比我们期望的还要好。
另外,可能同样重要的是:我们发现,尽管不同查询的结果相差很大(有些联邦查询比直接数据访问快,而另外一些有点慢),但是基于联邦的 servlet 的性能比直接访问数据的 servlet 更具竞争力。当然,根据发出的特定查询和实现的 servlet 设计,结果可能会不同。但是您将有机会回顾我们的工作,这样您就会明白它可能是怎样反映您组织中常见的那类工作。
关键技术概述
理解联邦 DBMS 技术和 servlet 是理解我们项目的关键。这里我们将只描述这两者的几个要点,因为许多网站(包括本网站)已经发布了无数有关这些主题的教程和技术文章。如果您已经很熟悉联邦 DBMS 和服务器端 Java 技术,那么跳过这一节,开始阅读 我们做了什么。
理解联邦 DBMS
联邦 DBMS 提供了到多个数据源的单一应用程序编程接口(API)。这些数据源可能运行在不同的硬件和操作系统平台上,可能是由不同供应商开发的,也可能使用了不同的 API(包括不同的 SQL“方言”)。程序员使用联邦服务器以比本来可能的抽象级别更高的级别工作,因为该服务器提供了物理上异构的数据的单点映像。使用表(或其它数据对象,如文件)的 昵称(nickname)为程序员提供了位置透明性,使他们不必确切知道所期望的数据驻留在何处。功能补偿可以掩盖不同供应商产品之间的差别,并模拟不受给定数据源本机支持的能力。多点连接(join)和联合(union)促进了来自多个数据源的数据的集成。
各个商业产品的数据源支持和产品功能各不相同。DB2 Information Integrator 支持 IBM® DB2 系列的所有成员、IBM Informix® 、Microsoft® SQL Server、Oracle、Sybase、支持 ODBC 的数据源、XML、Web 服务、WebSphere® MQ、Excel 电子表格、平面文件以及生命科学数据源。此外,DB2 Information Integrator 还可以通过 IBM Lotus® Extended Search 访问 Web 搜索引擎、内容资源库、电子邮件数据库和其它基于内容的数据源。
因为 DB2 Information Integrator 包含功能齐全的关系 DBMS,所以它可以存储和管理它自己的本地数据对象,如表、视图和索引。它的优化器被设计成考虑其环境的异构和物理分布的特性,这样它可以为每个查询选择一个有效的数据访问策略。这个 DB2 Information Integrator 发行版支持在单个事务内从多个数据源读取数据;支持针对每个事务一个数据源的写操作。
理解服务器端 Java 组件
在许多 Web 应用程序服务器环境中,很多公司会经常使用 servlet 和 Enterprise JavaBeans(EJB)以实现关键的业务逻辑,包括对可能驻留在远程服务器上的企业数据的访问。servlet 是这两种技术中较早出现的一种,它们经常被用作公共网关接口(Common Gateway Interface,CGI)脚本的替代品,以支持 Web 页面的动态内容。servlet 依靠请求-响应编程模型。
Java 编程环境提供了包含用于编写 servlet 的接口和类的包,并对它们的构造强加了特定的编程要求。例如,servlet 包含了基本的生命周期方法(如 init、 service和 destroy),服务器会在适当的时候自动调用它们。程序员可以根据需要覆盖这些方法,通常他们确实也是这样做的。例如,我们的 servlet 在初始化时查找 DataSource 对象(用于连接)以使开销最小。大多数 servlet 工作的大部分在其 doGet和 doPost方法中进行。我们的 servlet 使用这些方法来执行适当的查询并返回结果。
EJB 是实现最少行为集的服务器端软件组件,它们以可以简化应用程序开发和有助于提升可移植性的方法来封装业务逻辑。对生产应用程序常常需要的功能(如事务、安全性和持久性)的支持是由 EJB 规范规定的。这个网站上的其它文章描述了如何将实体 EJB 和 DB2 Information Integrator 集成在一起,所以这里我们将不讨论这个主题。(更多信息,请参阅 针对各种全异的数据源开发实体 EJB 的经验之谈和 Accessing Federated Databases with Application Server Components。)但值得注意的是,可以轻松地将我们为这个项目构建的 servlet 转换成会话 EJB。实际上,我们在多个例子中都这样做了,并发现开发成本和性能问题大致相同。
我们做了什么
我们想要探究使用联邦 DBMS 技术开发需要集成不同数据源数据的 servlet 的优缺点。而要真正那样做的唯一方法是在使用联邦 DBMS 和不使用它的情况下,设计、编码和测试一系列执行相同功能所必需的 servlet。
我们的环境
我们构建了几个 servlet,它们和以下软件一起工作:
WebSphere Studio,包括 WebSphere Application Server 5 测试环境
Oracle DBMS 和相关联的客户机软件
DB2 DBMS 和相关联的客户机软件
Microsoft Excel 电子表格
Sun Microsystems JDBC/ODBC 驱动程序(用于对 Excel 的 Java 访问)
DB2 Information Integrator(早期发行版)和相关联的客户机软件
图 1演示了总的软件体系结构。正如您可以看到的,我们的 WebSphere Studio 平台被配置成使用 DB2 Information Integrator,后者又被配置成访问 DB2 Universal Database™(UDB)、Oracle 和 Excel 数据源中的数据。这代表了联邦 DBMS 体系结构。我们还配置了 WebSphere Studio 平台以直接对 DB2 UDB、Oracle 和 Excel 数据源进行操作。这个配置代表了本机数据访问环境。
图 1. 用于我们项目的软件体系结构
我们的应用程序方案
我们工作的业务方案涉及了虚拟的分销公司的合并。我们假定新统一的公司需要开发一个其关键业务数据的集成视图,这些数据以不同的格式存储在不同的系统中。此外,由于需要支持公司合并以前开发的应用程序,所以使数据保留在其每个本机平台上,这一点很重要。对于评估使用联邦 DBMS 技术如何支持需要涉及这些不同数据源的新应用程序而言,这个业务需求是一个很好的例子。
我们的数据
我们对我们的工作使用了 TPC-H 数据的子集,并把这些数据分布在多个数据源以模拟多个部件分销商合并成一个公司的情况。在我们的例子中,这些数据源包括 DB2 UDB 和 Oracle DBMS,以及 Excel 电子表格。我们使用的特定 TPC-H 表是 PART、PARTSUPP、SUPPLIER、NATION、CUSTOMER 和 ORDERS。对于那些不熟悉 TPC-H 数据库的人来说, 图 2显示了这些表的结构以及它们是如何相互联系的。有关详细信息,请访问 http://www.tpc.org。
图 2. 用于我们项目的 TPC-H 数据库模式子集。星号(“*”)表示主键列。
数据库模式" src="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/0305saracco1/images/fig-2.gif" width="467" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/"/>
我们使数据分散分布,以便各种数据源上有一些冗余的部件、供应商和客户数据。我们推断:不同的分销商很可能会共享某些相同的客户、供应商和产品。例如,虽然某个特定的业务伙伴可能从多个分销商那里订购部件,但该公司不可能向每个分销商下相同的订单。因此,尽管有些 CUSTOMER 信息在不同的数据源是相同的,但这些客户的特定 ORDERS 并不相同。
请注意:尽管我们的工作使用了一部分 TPC-H 模式,但我们并未试图实现任何 TPC 基准测试。而是选择实现一些查询,我们认为这些查询会反映 J2EE 程序员为支持新合并的部件分销企业而可能必须实现的工作。不过在下一节中我们将讨论更多相关内容。
我们的查询
我们决定实现多个查询,其中的一些类似于可能在 Web 商业或决策支持环境中执行的查询。我们所有的查询都被设计成处理先前提及的表。最终,我们觉得我们的查询模拟了两个典型的业务方案:联机搜索和业务分析。
第一组查询 查询 1 - 3支持联机目录搜索。这些查询使用户或业务伙伴能够找到具有某些特征的部件,然后确定在给定国家或地区中所选部件售价最便宜的供应商。最后一个查询确定该用户是否是给定时间段内订货量最大的 10 个客户之一,因为如果是的话,那么他或她就有资格享受免费运送或折扣。
第二组查询 查询 4 - 5支持很可能在合并后的方案中着手进行的业务分析工作。这些查询尝试确定两个或多个新合并公司的共同客户,这些新并入的公司共同具有某些值得注意的特征 - 它们可能属于同一个目标群体、同属于竞争力很强的领域等等。
接着是我们的目标查询,其中包含了一些样本搜索谓词。我们 servlet 中的实际查询稍有不同。例如,我们使用了参数标记,以便于查询更灵活,而且我们必须更进一步修改某些查询,特别是当我们直接访问三个数据源中的各个数据源时。在后续文章中您将有机会看到最终的查询。目前,重点只是理解我们需要实现的查询的本质。
当在这里读到我们的查询时,您应该假定 FROM 子句中引用的对象涉及所有数据源上的数据,除非其名称是用特定的数据源名称作前缀的。例如,我们使用“part”表示已合并公司的所有部件数据,而不管它可能驻留在哪里。我们使用“db2_customer”表示特定于一个合并前的公司(本例中,是将 DB2 用作其 DBMS 的公司)的客户数据。
查询 1: 查找感兴趣的 partkey:
select p_name, p_mfgr, p_type, p_partkey
from part where
p_type like '%BURNISHED%' and
p_name like '%lavender%'
order by p_partkey
fetch first 20 rows only;
查询 2: 查找在给定国家或地区(本例中是 GERMANY)中特定 partkey(本例中是 28)的售价最便宜的供应商:
select ps_partkey, s_name, s_suppkey, min(ps_supplycost)
from partsupp, supplier, nation where
ps_partkey = 28 and
ps_suppkey = s_suppkey and
s_nationkey = n_nationkey and
n_name = 'GERMANY'
group by ps_partkey,s_name, s_suppkey;
查询 3:奖励那些在给定时间段内下了大量订单的客户:
select sum(o_totalprice)as totalordered, count(*) as num_orders, c_custkey, c_name
from customer, orders where
o_custkey = c_custkey and
O_ORDERDATE >= DATE('1997-10-01') and O_ORDERDATE < DATE('1998-10-01')
group by c_custkey, c_name
order by totalordered desc
fetch first 10 rows only;
查询 4: 查找选定国家或地区中某个分公司的客户,该客户向另一个分公司下了非常大的订单。本例中,有关受关注的客户和国家或地区的信息驻留在 DB2 DBMS 中,而有关它们可能向另一分公司下的订单信息驻留在 Oracle DBMS 中。
select c_custkey, c_name, o_totalprice, n_name
from db2_customer, ora_orders, db2_nation where
c_nationkey = n_nationkey and
c_custkey = o_custkey and
o_totalprice > 450000 and
n_name in ('JAPAN', 'CHINA', 'VIETNAM', 'INDIA');
查询 5: 查找一个分公司的限定客户对整个(合并后的)公司所下订单的平均订单额。限定客户是那些位于给定国家或地区(本例中是 Japan)中属于特定市场部分(HOUSEHOLD)并且帐户结余较低的人。本例中,有关限定客户及国家或地区的信息都驻留在 DB2 DBMS 中。
select avg(o_totalprice) as avg_order, c_name, c_custkey, c_acctbal
from db2_customer, orders, db2_nation where
c_custkey = o_custkey and
c_nationkey = n_nationkey and
n_name = 'JAPAN' and
c_mktsegment = 'HOUSEHOLD' and
c_acctbal >= 0 and c_acctbal <= 1000
group by c_custkey, c_name, c_acctbal
order by avg_order desc;
我们的联邦数据库对象
在定义了我们的软件体系结构和应用程序目标后,需要设计我们的联邦数据库。我们创建了一些对象,这些对象促进对 DB2 UDB、Oracle 和 Excel 数据进行透明访问。这些对象包括由每个数据源管理的 TPC-H 数据的昵称,以及基于这些昵称的 UNION ALL 视图。例如,为了支持查询 1,我们在 DB2 Information Integrator 服务器上定义了三个昵称,以表示三个数据源中每一个的 PART 数据。接着我们定义了 UNION ALL 视图,它包括这三个昵称中的所有列。通过这种方式,分散在 DB2、Oracle 和 Excel 中的 PART 数据就好象驻留在我们的联邦数据库的一个逻辑表中。
严格地说,并不是每个查询都需要 UNION ALL 视图(保留了重复行);例如,在不危及我们工作完整性的情况下,原本可以对 UNION 视图(没有重复行)轻松地执行 查询 1。实际上,最初编写 查询 1时,我们就考虑到了 UNION 视图;如果您仔细查看我们上面的示例,就会发现我们没有包含 DISTINCT 子句,对 UNION 视图而言它是多余的。
但是,最终我们决定对所有工作都使用 UNION ALL 视图。特别是,这使我们能够对所有需要使用合并数据的查询(包括 查询 1 - 3和 查询 5)都保持一致的视图定义。
我们的基本 servlet 设计
我们使用标准 JDBC 调用构建了 servlet,以通过 DB2 Information Integrator 和本机客户机接口(不使用 DB2 Information Integrator)来访问数据。只要可能,我们就使用 DataSource 连接(池化的连接),一般这会更有效地使用系统资源。通过在每个 servlet 的 init方法中执行对这些 DataSource 的 JNDI 查询,我们试着进一步维护代码的效率。
在通过 DB2 Information Integrator 访问远程数据时,我们的 servlet 对昵称或涉及多个昵称的 UNION ALL 视图发出查询。在直接访问远程数据时,我们的 servlet 逐个对每个数据源发出查询。接着我们必须确定如何将每个查询返回的结果集成在一起,以满足最初的目标。
我们本可以在我们的 servlet 中手工完成集成工作,即根据需要编写必要的代码以对数据进行合并、排序、聚集和分组。但是,这需要进行大量实质性的工作。而我们选择了使用本地 DBMS 表来协助我们的工作,因为我们推断大多数 WebSphere 开发人员会使用这样的软件。从每个数据源检索到适当的数据后,我们的 servlet 将结果插入到本地 DB2 数据库的辅助表中,然后查询这些表以获得最终结果。当然,我们尝试过在远程数据源上尽可能多地过滤数据,以使网络流量达到最小,并进一步改善我们本机 servlet 的效率。
如果这听起来有点混乱,那么请考虑一个简单示例。想象一下我们需要构建一个 servlet,在不使用 DB2 Information Integrator 的情况下,它会报告一套 partkey 的供应商最低价。在 servlet 与三个数据源的每一个连接之后,它会查询每个源上的 PARTSUPP 数据以查找那些 partkey 的供应商最低价。(SQL 语句看上去就象 Select MIN (ps_supplycost) from PARTSUPP where ps_partkey IN ( list of ps_partkeys) group by ps_partkey 。)然后 servlet 将价格信息存储在本地临时表中,该表有两个(非唯一)列:ps_partkey 和 ps_supplycost。最后,servlet 会对这个本地表发出查询以计算根据 ps_partkey 分组的 ps_supplycost 的最小值。
这是我们为实现使用直接数据访问的 servlet 而采用的基本逻辑。当然,该逻辑比只编写单一查询复杂,当我们使用 DB2 Information Integrator 进行数据访问时我们已采用了这样的单一查询。但是复杂程度到底如何呢?请继续往下读。
我们学到了什么
至此,您可能想知道我们从工作中学到了什么。我们将在本文中总结我们的结论,并在随后的两篇文章中向您显示我们是如何得出这些结论的。但最终结果是:我们发现,当使用 DB2 Information Integrator 时,构建 servlet 轻松得多。
为什么呢?因为使用了 DB2 Information Integrator(II):
我们要实现的数据访问逻辑更少。这使得代码行减少了大约 40%。
我们不必担心如何针对每个目标数据源正确分解我们的查询。这为我们节省了无数时间,并避免了单调的调试工作。它还让我们更轻松地获得我们想要的结果!
我们不必对数据访问逻辑作过多的调优。例如,我们不必为了确定合适的连接处理逻辑而考虑如何分布数据。我们让 DB2 II 的优化器为我们完成这项工作。它一般会完成得非常好,在某些情况中,比我们自己手工编码的效果还要好。
设计数据访问逻辑
因为我们的 servlet 是数据密集型的,所以其大多数代码都与数据访问逻辑有关。使用 DB2 Information Integrator,我们的 servlet 连接到一个数据库服务器,发出一个查询,并释放它们已获得的很少的资源集。不使用 DB2 Information Integrator,我们的 servlet 分别连接到每一个数据源,对每个数据源至少发出一个查询,将从每个数据源检索到的数据放到至少一个本地辅助表中,对该辅助表至少发出一个(最终)查询,清除该辅助表的内容,并释放它们已获得的所有本地和远程资源。
当然,这需要更多代码来实现。而且,在不使用 DB2 Information Integrator 的情况下实现我们的 servlet 所花的时间是使用 DB2 Information Integrator 的两倍多,因为所涉及的工作很复杂。而且大量复杂性涉及到确定如何适当分解针对每个数据源的每个目标查询。
进行正确查询
在直接使用不同的数据源时,我们知道我们必须对每个数据源发出查询,合并结果并执行一些最终处理以返回我们寻求的信息。确定对每个远程数据源发出 什么查询并非易事,认识到这一点并不难。
为了确保本机实现具有合理的性能,我们知道,我们需要在每个后端数据源上尽可能多地过滤数据。例如, 查询 2 包含 WHERE 子句,它指定了特定的 partkey 值。这种搜索谓词具有相当大的选择性,因此应该下放到数据源上去进行,以避免不必要的数据传输。
不幸的是,并不是所有查询都可以象这样简单地交由每个数据源处理。需要谨慎对待使用某些聚集函数和 FETCH FIRST n ROWS 子句的查询。在某些情况中,将它们下放给每个后端数据源可能会在尝试合并最终结果时产生不正确的信息。
如果那听起来有点混乱,那么我们将在下一篇文章中带您查看一个详细示例。我们将向您显示在不危及查询语义安全的情况下,怎样才能不下放 查询 5 中的 AVG 函数。而是必须对我们的后端数据源查询恢复使用 SUM 和 COUNT(*) 函数。接着,在将结果合并到辅助表中后,我们将这两列中数据相除,从而算出每个客户的平均值。
因此,确定如何正确地分解复杂查询以便不影响性能以及不危及我们工作的完整性,这会是一个耗时且易出错的过程。但是,值得注意的是,DB2 Information Integrator 将这个艰巨的任务作为其全局查询优化工作的一部分替您自动处理了。
确定数据访问策略
我们的一些查询涉及了不同站点上两个(或多个)表的连接。特别是, 查询 4 和 5都具有这个特征。如果您熟悉关系 DBMS,那么应当知道所有主要的商业产品都支持多连接方法以便进行有效处理。表大小和适当列上的索引的可用性会显著影响 DBMS 优化器将针对给定查询而选择的连接方法的类型,以确保合理的性能。
不使用联邦 DBMS(及其全局优化器)的情况下,如果需要连接不同数据源上的数据,那么您必须负责确定如何有效地处理连接。最简单的方法 - 检索每个数据源上的所有行(或所有容易限定的行)并在本地管理连接 - 可能产生糟糕的性能。但是确定最佳方法也并不总是这么容易,特别是如果您无权访问全局目录(带有关于远程表大小和这些表索引的可用性的统计数据)时。
对于有些查询,我们不得不面对这个问题,迅速认识到有关数据分布的不同假定可以引导我们以不同的方式实现我们的 servlet 逻辑。如果数据分布要随着时间变化而发生更改,如果创建了新索引,或如果删除了现有的索引,则我们 servlet 的性能会下降,并可能迫使我们再次更改代码。这是使用联邦 DBMS 如何花更少的编程精力(和更少的技能)使我们能够完成开发工作的另一个示例。
结束语
构建需要集成多个源上数据的 Web 组件并不是一项轻松的任务。幸运的是,联邦 DBMS 技术最近的发展有助于缓解与之相关的众多负担。
我们构建了需要集成多个系统上数据的 servlet。其中一组使用了 DB2 Information Integrator,而另一组直接访问每个数据源。我们的经历表明:当我们使用 DB2 Information Integrator 来提供对不同数据源的透明访问时,可以明显减少设计、开发和维护工作。在我们的例子中,我们削减了大约 40% 的代码行。此外,我们无需费力且耗时地去尝试确定如何有效地将查询的各个部分分配给每个数据源,以最小化不必要的数据传输,同时仍保证工作的完整性。依靠 DB2 Information Integrator 固有的全局优化能力,我们实现了这一点,这些能力允许我们获得正确的工作结果集,同时又保持合理的性能。
在以后的两篇文章中,我们将讨论我们工作的更多细节。首先,我们将带您查看一些代码样本,并讨论详细的开发问题。最后,我们将评论性能问题并向您显示一些性能数据。下次见 - 会有更多内容。
作者简介
C. M. Saracco是 IBM 硅谷实验室的高级软件工程师以及 UC Santa Cruz 扩展计划的前任软件技术讲师。她已经就各种技术主题在北美、南美、欧洲和中东举行了演讲。
Susanne Englert是 IBM 硅谷实验室的高级软件工程师,她在软件性能评测和分析方面具有 15 年的经验,尤其擅长针对大型数据库进行复杂查询的性能和优化。她在 1996 年和 2000 年期间担任事务处理性能委员会(Transaction Processing Performance Council,TPC)决策支持附属委员会的主席。
Ingmar Gebert正在德国的 Rostock 大学攻读计算机科学和商务。他最近在 IBM 硅谷实验室完成了实习,在那里他调查了用于开发访问异构数据源的 servlet 和会话 EJB 的不同技术。