内容


联邦 - 数据库互操作性(第 1 部分)

简介

如果您有多种数据库,那么您应该会发现让它们相互通信是一件麻烦事。本文将通过解决数据库之间的互操作性来帮助您更方便地处理这一问题。我们将使用 IBM 战略性联邦数据库技术 - IBM® DB2® 信息集成器(DB2® Information Integrator)的核心。我们将研究该技术的工作原理、关键的技术成功因素以及在针对这些因素进行测试时如何证明 IBM 实现有效。在本文的第 2 部分中我们还将研究插入性能以及如何联邦服务器,以便从存储在多个数据源的数据中生成 XML。

为何我需要数据库联邦,另外什么是数据库联邦?

作为一位 DBA,您或许不愿管理许多不同品牌的数据库 - 我还没碰到过有 DBA 这么说:“是的,不错,我想要各种不同的数据库,我还不够忙。”如果您可以使用所有不同的数据库,包括选择、插入、更新和删除,就象所有的表都位于单个数据库中那样,那么将获得非常高的工作效率。数据库联邦就是要做到这一点:使所有表看起来象是在同一个数据库中。下面的样本 SQL 正是说明了这项技术是多么的强大:

db2 => insert into remote_informix
select a.customer, b.balance, c.limit
from remote_db2 a, remote_oracle b, remote_sybase c
where c.limit  > 10000
and a.c_key = b.c_key
and a.c_key = c.c_key;

数据库联邦可以使我们不再需要构建数据集市了!一个数据集市替代品的性能最优化示例 如下所示。如果要查询的量不是很大,并且如果汇总表通常就可以满足查询的要求,那么就不需要数据集市、不需要相应地创建新服务器和移动大量的数据等,这样可以大大地提高工作效率。当然,对于需要访问最低级别的详细信息的忙碌繁重的查询而言,数据集市或数据仓库是首选的解决方案。

数据库联邦如何工作?

真是个好东西。哦,不好意思,该讲讲技术方面的内容了吧?

请考虑如下所示的系统图:

图 1. 联邦成员
图 1
图 1

“联邦者(federator)”系统对远程系统“被联邦者(federatee)”中的表进行操作。远程表在“Federator”数据库中作为虚拟表出现。客户机应用程序可以对“Federator”数据库中的虚拟表执行操作,但是真正的持久性存储位于远程数据库中。我们将研究一个样本客户机程序,在本文的下一部分中该程序执行插入。

每个“被联邦者”都将“联邦者”看成是另一个数据库客户机连接。“Federatee”只是处理数据库操作的客户机请求。“联邦者”需要用客户机软件来访问每个远程数据库。要访问每种被联邦者,则需要安装 IBM Informix®、Sybase、Oracle 等等的客户机软件。

数据库联邦的应用程序接口是 SQL。与必须学习新接口相比,这大大地提高了工作效率。使用与对本地表进行选择、插入、更新和删除相同的语法来访问远程表。并不是可以进行所有表操作,但是 DB2 V8 中的信息集成器通过提供插入和更新功能在这方面取得了很大的进步。

安装和配置

这是安装和配置过程的总结和重点。别忘了阅读手册。您将需要安装带有信息集成器选项的 DB2 V8.1。安装时请选择 Custom,然后在 Server Support选项下面启用“Informix Data Source Support”。Informix 包装器目前和 8.1 服务器一起提供;其它包装器还处于 beta 测试阶段,不久就可以使用了。有关适用于 V7 和 V8 的 Linux 内核参数的提示和技巧文章,请参阅我以前的文章: Simulating Massively Parallel Database Processing on Linux!有关配置 Informix 远程服务器的最佳实践,请参阅我的文章: Winning Database Configurations: An IBM Informix Database Survey

心急的话,请查阅 附录 A,其中包含了设置联邦环境的完整 SQL。

设置联邦的步骤如下:

步骤 1:设置联邦者和远程数据库服务器之间的客户机连通性

在试图进行联邦配置之前,需要使远程客户机软件可操作。测试一个简单的客户机程序(例如来自 Informix 的 demo1.ec ,或者是来自 Oracle 或 Sybase 的类似客户机程序)以验证连通性。如果客户机软件不能访问远程服务器,那么您就不能继续下去。

步骤 2:创建包含联邦者所需参数的 db2dj.ini 文件

DB2 V7 和 DB2 V8 都需要 db2dj.ini 文件。该文件为远程数据库提供参数和路径。这里所示的样本引用了两个远程服务器:一个是 IBM Informix Extended Parallel Server™(XPS),一个是 Informix Dynamic Server™(IDS)。下面是来自 Windows® 2000 机器的一个样本:

C:\\Program Files\\IBM\\SQLLIB\\cfg>type db2dj.ini
INFORMIXDIR=C:\\PROGRA~1\\Informix\\CLIENT~1
INFORMIXSERVER=fliif
INFORMIXSQLHOSTS=C:\\tmp\\sqlhosts
INFORMIXSERVER=flxps

该示例说明了如何访问两个远程服务器:一个是 IBM Informix V9,另一个是 IBM Informix XPS。

需要告知数据库如何获取该文件。请确保使用全限定路径名。别使用相对路径名 - 否则会很糟糕。对于 Windows 2000,请使用:

DB2_DJ_INI=c:\\\\progra~1\\ibm\\sqllib\\cfg\\db2dj.ini

对于 UNIX® 或 Linux,请使用:

db2set DB2_DJ_INI=/home/db2inst1/sqllib/cfg/db2dj.ini

步骤 3:为远程数据库创建“包装器”

“包装器”定义了一个库文件,该库文件知道如何与被联邦者数据库进行通信。它使用您在步骤 1 中所设置的客户机连通性软件来访问远程数据库。

定义包装器有两种方法:使用 SQL,或者在 DB2 V8 中使用图形用户界面(GUI)。

使用 SQL 创建包装器的示例如下所示:

CREATE WRAPPER "INFORMIX" LIBRARY 'db2informix.dll';

从控制中心(Control Center)启动 GUI 向导,如 图 2所示。在您想要配置的数据库的树型导航窗格中的 Federated Database Objects标识上单击鼠标右键。如插图所示。建议您在使用 GUI 时使用按钮 show SQL。它使您能比较好地理解幕后所发生的事情(请参阅 图 3,以了解一下示例)。将 show SQL按钮当成您的私人 DBA 老师。

图 2. 使用控制中心创建包装器
图 2
图 3. GUI 的幕后
图 3
图 3

步骤 4:定义联邦服务器 - 远程数据库实例

使用 CREATE SERVER 语句(或者控制中心)定义远程实例。请注意,我们使用了前一步中所创建的包装器。有许多选项可用来指定相对的 CPU 和 I/O 速度,以及网络速度和其它一些参数。IBM 建议您接受大多数参数的缺省值。下面所示的参数只用来举例说明该如何指定这些参数。

下面是 SQL 示例:

CREATE SERVER "rcfliif" 
TYPE INFORMIX VERSION '9.3' 
WRAPPER "INFORMIX" OPTIONS ( NODE 'fliif', 
	DBNAME 'stores_demo'
	 , ADD CPU_RATIO '1'
	 , ADD IO_RATIO '1'
	, ADD COMM_RATE '1'
	, ADD DB2_MAXIMAL_PUSHDOWN 'Y' 
);

NODE 指定了远程数据库服务器,它不是一个 TCP/IP 主机名。DBNAME 定义了远程数据库。

PUSHDOWN 是缺省值,它表明如果可能的话应当在远程服务器上进行连接(join)。我们将对此进行测试并在下一节中研究某些数据库优化器说明计划。DB2_MAXIMAL_PUSHDOWN 是一个无文档说明的参数(此处有说明),它告诉联邦者发送 SQL 并连接“被联邦者”,即使优化器认为在本地抽取数据和进行连接会比较好。

您可能想使用的其它选项包括 FOLD_ID 和 FOLD_PW,两者均设置成“N”。这使得被联邦者使用在“用户映射”(User Mapping)中完全相同的用户标识和密码连接数据源(请参阅步骤 5)。如果没有设置,联邦服务器会尝试连接四次,第一次尝试用全是大写的用户标识和密码进行连接,第四次尝试用全是小写的用户标识和密码进行连接。如果用户标识和密码全是小写的(这在 Unix、Linux 和 Windows 系统的情况中较常见),那么将 FOLD_ID 和 FOLD_PW 设置成“N”会使连接数据源变得快一点。

步骤 5:为联邦者认证创建用户映射

联邦者想从远程数据库获取数据。它需要象任何其它客户机程序那样进行认证。“用户映射(User Mapping)”提供了建立数据请求凭证的机制。

将本地 db2 用户标识映射到远程服务器上的用户标识。下面这个示例从本地标识“LURIE”映射到远程“Informix”标识。

CREATE USER MAPPING FOR "LURIE" 
SERVER "rcfliif" OPTIONS( REMOTE_AUTHID 'informix', REMOTE_PASSWORD 'useyourown');

DB2 控制中心还提供了用户映射的 GUI 定义方式。如图 4 所示:

图 4. 使用控制中心映射用户标识
图 4
图 4

步骤 6:表别名 - 访问远程表的“门票”

到目前为止我们已经实现了以下内容:

  • 包装器:定义了服务器类型并标识了访问它的二进制文件。
  • 服务器:定义了远程服务器和数据库在该服务器上的位置。
  • 用户映射:定义了到远程服务器的认证。

现在我们就只需要一些表了!

通过别名(nickname)将每张表定义到 Federator。一旦定义了别名,我们就可以象使用本地表名那样使用它。下面的示例说明了属于远程服务器“rcfliif”上用户“lurie”的远程表“card1”被定义成别名“rc9_card1”。

create nickname rc9_card1 for "rcfliif"."lurie"."card1";

恭喜恭喜!有了合适的别名,我们现在就可以做一些“激动人心”的事情了,比如:

select * from rc9_card1;

表名可以提供有用的标识。这个命名约定相当简单。“rc”是关系连接(Relational Connect)的缩写。“9”表示远程服务器是 Informix V9。最后的“card1”是表名。这张表有几个不同基数的生成列。请参阅 附录 B,以获取用于创建表的源代码。

我相信现在 GUI 迷正翘首企盼有一个抓屏,我绝不会让你们失望的。首先是一个过滤器对话框样本,它将表限制为用候选的别名表示( 图 5):

图 5. 过滤表
图 5
图 5

过滤处理之后显示了一个表的列表( 图 6)。在本例中只有一张表满足了过滤器条件。

图 6. 满足过滤器条件的表
图 6
图 6

单击 OK,然后会出现 图 7,这是一个数据样本,来自新建别名所代表的表。

图 7. 满足过滤器条件的表
图 7
图 7

联邦连接

配置服务器联邦之后该进行某项测试了。我所使用的环境如 图 8所示。创建该环境的 SQL 列在 附录 A中。

我们要研究的第一个查询将使用远程 IDS9 服务器中的两张表:“rc9_card1”和“rc9_card2”。然后我们将使用具体化数据高速缓存方面的一些先进技术对四张远程表进行最优化。

图 8. 我的测试环境
图 8
图 8

在何处执行连接?

由联邦者基于成本的优化器选择在何处处理表连接。这个选择是影响联邦性能最重要的几个方面之一。优化器可以选择将 SQL 发送到远程服务器并使用远程引擎来连接表。这就是所谓的 下推操作(pushdown)。另外,联邦者可以从远程服务器检索各行,并且可以使用联邦者连接引擎执行连接。

让我们进行查询:

select a.c3, a.c10, sum(a.c100 ) as sum_c100, sum(a.c1000) as sum_c1000 
from rc9_card1 a, rc9_card2 b
where a.superkey = b.superkey
group by a.c3, a.c10;

图 9演示了从远程服务器抽取各行然后在联邦者引擎中处理它们的说明计划。这个连接的总成本超过 4500 万 timeron。

图 9. 在不使用下推操作的情况下进行联邦连接的计划
图 9
图 9

确定在何处执行连接为最佳方案时要考虑几个重要因素:

  • 联邦者 CPU 的相对速度与被联邦者 CPU 的相对速度的对比
  • I/O 子系统的相对速度和吞吐量
  • 网络速度
  • 被联邦者数据库的功能

控制中心中的 Create Server配置显示了许多可以根据服务器相对速度进行调优的参数( 图 10)。

图 10. 使用控制中心调优某个特定被联邦者所使用的性能参数
图 10
图 10

根据经验,下推操作很有用,当被联邦者数据库能挣脱繁重的文件包束缚时,应当尽可能多地进行下推操作。对于薄弱的远程数据源(比如平面文件集),最好(有时候是 必须)通过检索所有行并连接到联邦者服务器进行连接。

进行下推操作后,查询计划看起来大不一样,如 图 11所示。与不进行下推操作的情况相比,总成本(timeron)大大降低了。

优化器实际上非常深入地考虑了在何处进行连接。我努力使这项工作尽可能简单。不要过分担心弄不懂分布式连接理论 - 有太多有趣的联邦技术的应用,以至于很难详述该理论。联邦服务器和远程服务器的最新统计将改进该查询计划。

图 11. 下推操作连接执行得快得多
图 11
图 11

更快的联邦连接 - 具体化查询表

连接两张表最快的方法是根本就不连接它们。事务处理委员会(Transaction Processing Council,TPC)的 TPC-D 基准测试(现已撤销)最后证明:如果在汇总表中预先计算查询答案,那么该查询会运行得比较快。

如何才能将这种汇总表加速技术应用到联邦环境呢?要真正充分利用汇总表,必须尽可能随时使用它们。一个月一次的数据汇总可以满足一个月、一个季度或一年一次聚集级别的查询。但是优化器必须智能到能够重写查询,以便对汇总表进行操作而不是寻找详细情况。我们将测试这项功能。

将汇总表添加到联邦者的方式与非联邦环境非常相似。在 DB2 V8 中这些表被称为具体化查询表(MQT)。联邦者的一个强大功能就是在别名(远程)表上创建 MQT。

添加具体化查询表

MQT 包含了一条 SELECT 语句,用于定义该如何汇总数据。实现 MQT 的 SQL 如下所示:

--create a materialized query table or MQT
-- this will allow the optimizer to rewrite SQL
-- and not access the remote servers
drop table card_mqt;
create summary table card_mqt 
as (
select a.c3, a.c10, sum(a.c100 ) as sum_c100, sum(a.c1000) 
as sum_c1000 
from rc9_card1 a, rc9_card2 b, rc8_card1 c, rc8_card2 d
where a.superkey = b.superkey
and b.superkey = c.superkey
and c.superkey = d.superkey
group by a.c3, a.c10 )
data initially deferred refresh deferred;
-- populate the MQT with data
refresh table card_mqt;
-- VERY IMPORTANT - tell the optimizer the MQT
-- alive and well and open for business
set current refresh age=any;

那么 MQT 提供了多大的改进呢?

首先考虑跨两个不同服务器的四表查询。不进行下推操作的查询成本最高。查询 SQL 如下:

select a.c3,  sum(a.c100 ) as sum_c100
from rc9_card1 a, rc9_card2 b, rc8_card1 c, rc8_card2 d
where a.superkey = b.superkey
and b.superkey = c.superkey
and c.superkey = d.superkey
group by a.c3;

选中该查询是因为它使用的聚集级别与我们刚刚定义的 MQT 不一样。这给优化器增加了难度 - 我们想知道优化器是否智能到能够重写查询以使用 MQT 而不是抽取网络上的所有数据。

不使用 MQT 的查询计划类似于 图 12

图 12. 不使用 MQT 时的查询计划
图 12
图 12

现在让我们添加 MQT。毫无疑问,优化器非常智能,足以重写查询以使用汇总而不是依靠“蛮力”得出答案。成本急剧削减到只需 25 timeron( 图 13)。

图 13. 由于使用 MQT 查询性能大大改进
图 13
图 13

再来看看使用 MQT 发生了什么:控制中心的可视化说明(Visual Explain)工具显示了提供给联邦者的 SQL 和实际执行的 SQL。

提交的 SQL 查询引用被联邦者上的远程表。在列 c3 上执行分组。这给优化器增加了难度,因为 MQT 是在列 c100 上进行聚集的。

图 14. 提交给联邦者(最优化之前)的查询
图 14
图 14

现在,看看 图 15。最优化之后连接没有了!从 MQT 中进行单表 select 操作满足了查询。优化器使用系统元数据来确认表 card_mqt 需要进行简单的聚集而不是使用远程表。

图 15. 实际执行的查询使用了 MQT
图 15
图 15

消除数据集市

优化器查询重写操作与联邦服务器上的 MQT 一起使用,这是一个功能非常强大的组合。无需构建数据集市的所有工作,MQT 为许多查询提供了另一种方法。在查询使用禁用汇总高速缓存的函数和大量数据的组合时,这不能替代数据集市。

应当在 MQT 闲暇时填充并刷新它,以最优化性能并将计算和通信流量的影响降到最小。

结束语

以上便是对联邦的简介。在下一篇文章中我们将研究应用联邦技术的其它方法,具体说来就是:

  • 联邦插入
  • 根据联邦连接产生的 XML,以及
  • XML 和 Websphere® MQ:巧克力配花生酱。

附录 A

-- This SQL creates a federated environment 
-- for two different 
-- Federatees
-- connect to the DB2 database
connect to sample;
drop wrapper "INFORMIX";
CREATE WRAPPER "INFORMIX" LIBRARY 'db2informix.dll';
-- create a server for a remote instance of Informix 9.3
CREATE SERVER "rcfliif" 
TYPE INFORMIX VERSION '9.3' 
WRAPPER "INFORMIX" OPTIONS ( NODE 'fliif', 
	DBNAME 'stores_demo'
	-- , ADD CPU_RATIO '0.0000001'
	-- , ADD IO_RATIO '0.0000001'
	 , ADD CPU_RATIO '1'
	 , ADD IO_RATIO '1'
	, ADD COMM_RATE '1'
	-- no, still not pushing down join , ADD PUSHDOWN 'Y' 
	, ADD DB2_MAXIMAL_PUSHDOWN 'Y' 
	--  , ADD PUSHDOWN 'Y' 
);
-- create a server for a remote XPS Informix v8.3 instance
CREATE SERVER "rc_xps" 
TYPE INFORMIX VERSION '8.3' 
WRAPPER "INFORMIX" OPTIONS ( NODE 'flxps', 
	DBNAME 'stores_demo'
	, ADD CPU_RATIO '1.0'
	, ADD IO_RATIO '1.0'
	, ADD COMM_RATE '2'
	, ADD PUSHDOWN 'Y' 
);
--
CREATE USER MAPPING FOR "LURIE" 
SERVER "rcfliif" OPTIONS( REMOTE_AUTHID 'informix', 
  REMOTE_PASSWORD 'useyourown');
CREATE USER MAPPING FOR "LURIE" 
SERVER "rc_xps" OPTIONS( REMOTE_AUTHID 'informix', 
  REMOTE_PASSWORD 'useyourown2');
--
create nickname rc9_card1 for "rcfliif"."lurie"."card1";
create nickname rc9_card2 for "rcfliif"."lurie"."card2";
create nickname rc8_card1 for "rc_xps"."lurie"."card1";
create nickname rc8_card2 for "rc_xps"."lurie"."card2";
--create a materialized query table or MQT
-- this will allow the optimizer to rewrite SQL to 
-- the remote servers
drop table card_mqt;
create summary table card_mqt 
as (
select a.c3, a.c10, sum(a.c100 ) as sum_c100,
 sum(a.c1000) as sum_c1000 
from rc9_card1 a, rc9_card2 b, rc8_card1 c, rc8_card2 d
where a.superkey = b.superkey
and b.superkey = c.superkey
and c.superkey = d.superkey
group by a.c3, a.c10 )
data initially deferred refresh deferred;
refresh table card_mqt;
set current refresh age=any;
-- run various queries and capture explain plans
set current explain snapshot=yes;
values current timestamp;
select a.c3, a.c10, sum(a.c100 ) as sum_c100,
 sum(a.c1000) as sum_c1000 
from rc9_card1 a, rc9_card2 b
where a.superkey = b.superkey
group by a.c3, a.c10;
values current timestamp;
select a.c3, a.c10, sum(a.c100 ) as sum_c100,
 sum(a.c1000) as sum_c1000 
from rc9_card1 a, rc9_card2 b, rc8_card1 c, 
 rc8_card2 d
where a.superkey = b.superkey
and b.superkey = c.superkey
and c.superkey = d.superkey
group by a.c3, a.c10;
values current timestamp;
select a.c3,  sum(a.c100 ) as sum_c100
from rc9_card1 a, rc9_card2 b, rc8_card1 c, 
 rc8_card2 d
where a.superkey = b.superkey
and b.superkey = c.superkey
and c.superkey = d.superkey
group by a.c3;
values current timestamp;
set current explain snapshot=no;

附录 B

-- (c) Copyright 2003  Martin Lurie and IBM Corp 
 
drop table card1;
create table card1 ( superkey serial,
	c3 int,
	c10 int,
	c100 int,
	c1000 int )
in dbspc1;
drop table card2;
create table card2 ( superkey int,
	c3 int,
	c10 int,
	c100 int,
	c1000 int )
in dbspc1;
-- Stored procedure to populate the 
-- table drop procedure pop_card;
create procedure pop_card (tot_rows int)
	
	define rows_in integer;
	define c3cnt integer;
	define c10cnt integer;
	define c100cnt integer;
	define c1000cnt integer;
let rows_in=0;
for c1000cnt = 1 to 1000 step 1
	for c100cnt = 1 to 100 step 1
	for c10cnt = 1 to 10 step 1
for c3cnt = 1 to 3 step 1
	insert into card1 values ( 0,
				  c3cnt,
				  c10cnt,
				  c100cnt,
				  c1000cnt);
	let rows_in = rows_in + 1;
	if rows_in > tot_rows then 
		exit for;
	end if;
		end for; -- c1000cnt
		if rows_in > tot_rows then 
			exit for;
		end if;
end for; -- c100cnt
		if rows_in > tot_rows then 
			exit for;
		end if;
end for; -- c10cnt
		if rows_in > tot_rows then 
			exit for;
		end if;
end for; -- c3cnt
end procedure;
execute procedure pop_card ( 50000 );
select count(*) from card1;

相关信息

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management
ArticleID=19968
ArticleTitle=联邦 - 数据库互操作性(第 1 部分)
publish-date=08012003