Knut Stolze信息集成开发,IBM Germany
2004 年 4 月 本文作者 Knut Stolze 描述了一种方法,可以通过 DB2 UDB 中已有的复制工具复制空间数据,方法是先将空间数据转换成基于 LOB 的外部表现形式,然后复制这些 LOB。文中还包括了设置程序示例。
简介
IBM 提供了 DB2 Spatial Extender,用于在 DB2 Universal Database TM 关系数据库中存储、管理和分析空间数据。空间数据是用结构化的类型存储在数据库中的,这样才能为其复杂的属性建立正确的模型,也才能通过一种更简单、更可靠的方式来利用那些专用的多维索引方法。
DB2 Information Management 产品家族的另一个关键组件是 DB2 Replication 套件。这个套件可以对从多个数据库中选出的数据集进行维护。对数据进行的修改可以从一个数据库的某张表拷贝到另一个数据库中的另一张表。DB2 复制是 DB2 Universal Database v8 的集成特性。它的复制能力使之适合于处理 DB2 的内置数据类型,如 INTEGER、DOUBLE 或 VARCHAR 等。目前,复制中并没有对已有空间数据的内置支持,也不能在用户需要的时候直接将空间数据复制到另外的数据库中。
本文描述了一种方法,可以用 DB2 中已有的复制机制对空间数据进行复制,具体方法是先将空间数据转换成基于 LOB 的外部表现形式,然后复制这些 LOB。在目标数据库上,我们施行相反的转换过程,再次构造出 DB2 Spatial Extender 能够理解的几何图形。这种方法要求在设置复制的时候进行一些小的修改。我将在本文中逐步解释必要的设置过程,这样您就能很轻松地理解,并在您自己的环境中应用这种方法了。
理解空间数据与复制
空间数据的作用是,在对象-关系型数据库系统中用标量值的形式表示复杂的空间特性。所谓空间特性,是指点、线、多边形,以及上面这些特性的集合。有了 DB2 Spatial Extender,您就具备了必要的数据类型与功能,可以在 DB2 数据库中对空间数据实施管理。只要您的数据库启用了空间功能,您就可以回答诸如“所有具有一定收入的顾客离我的商店有多远?”或“我是不是在某段特定的时间范围和某块相同区域内规划了多个建筑群?我能否将其合并?”之类的问题。 DB2 Spatial Extender 定义了一组数据类型和功能,可用于管理空间数据库(几何图形),也可以帮助您充分利用存在于您的数据中的空间信息。
空间数据类型是通过类型层次来组织的,可为您提供更高的抽象级别以及更好的类型安全性。不准确地说,SQL 中的类型层次等同于面向对象编程语言中的类层次。 图 1 向您展示的是空间类型的层次关系。用蓝色背景表示的类型代表不可实例化的类型。这些类型就好比 Java 或 C++ 程序语言中的抽象类。其他所有的类型都是可实例化的。
图 1. DB2 Spatial Extender 的类型层次关系
DB2 Spatial Extender 中包含了 100 多个不同的函数,可用于对空间数据进行操作。比如说,您可以测试一个几何图形是否包含在另一个之中,或者,您可以计算两个几何图形之间的距离。本文无意叙述所有这些函数的功能。有关这方面的更多信息请参阅 DB2 Spatial Extender User's Guide and Reference。
很多种不同的用户情景中都用到了数据复制。举个例子来说,复制可以实现多个不同的用户组在公用的数据上独立工作,被复制的那部分数据仍然可以保持同步。复制也为高可用的实现提供了一种方法。
图 2 概要描述了 DB2 复制的基本工作原理。用户的应用程序发出针对某一源表(source table)的查询(SELECT)及数据更改语句(INSERT、UPDATE、DELETE)。这张表中的数据会复制到另一个数据库上的另一张表中,我们把后面这张表称为目标表(target table)。在源表中对数据所作的修改会记录在 DB2 日志中。异步的 Capture 进程会读取日志记录,当它检测到对源表进行了修改时,就从日志记录中将变化的数据取出,放到一张中转表中,这张表叫做变化数据表(change-data table,即 CD 表)。另一个进程 Apply 周期性地扫描 CD 表,如果其中出现新的信息,这个进程就将其读出,然后发布到目标表上。Apply 进程可以处理多个目标表。因此,DB2 复制的架构遵循经典的发布/订阅机制。
图 2. DB2 复制概览
在下一节中,我将要描述一种方法,它可以用 DB2 及其复制工具提供的一些特性对空间数据库进行复制。转换函数和转换组的概念在空间数据复制中的重要性更高,在 结构化类型的转换函数 一节中我们将对其进行更详细地讨论。在 捕获空间数据中的变化 一节中,我们要介绍如何通过设置,捕获源(捕获)数据库上的空间(或其他类型)数据中发生的变化。本节将展示 CD 表和视图的定义方法,并解释 Capture 进程的工作原理。在 准备目标数据库 一节中,我会告诉您,应该用什么样的方式准备目标数据库,Apply 进程才能直接将空间数据分别存储到对应的表中。为进一步简化源数据库和目标数据库的设置过程,我们实现了一个命令行工具,您可以从本文的“下载”部分找到这个工具。有关这一工具的详细信息将在 设置工具“db2se repl”一节中讨论。本文以若干条对 DB2、空间信息以及复制特性的注解结束。
总览图
复制组件 —— 包括 Capture、Apply、Monitor 以及 Replication Center 的管理接口 —— 在一般情况下的设置并不能支持结构化的类型,特别是空间数据类型。Capture 程序不能解释 DB2 日志记录中所保存的空间数据,因此,我们必须采取一组措施克服这一限制。DB2 及其复制工具中已经内置了一些功能,可用来实现这些措施。让我们从高层开始介绍。
当我们复制空间数据的时候,要先将其转换成 LOB 数据,更确切地说,是转换成二进制大对象(BLOB)。采用这种方式之后,复制工具就可以对一种已经支持的数据类型进行处理了,而且也并不知道这些 BLOB 数据实际上是空间数据。我们可以利用视图来实现这样的转换。我们可以在复制中定义视图,这样 Apply 程序在向目标进行完全更新的过程中就可以从一张来自源表的视图中取出数据,对于差量数据的更新则从 CD 表的视图中取出数据。视图实现了对源数据的“逻辑”扩展与/或解释,因此,Apply 程序就可以在获取数据的过程中对数据进行转换或扩展。在这样一种空间数据复制的实现方法中,我们针对源表定义一张视图,令其以 BLOB 的形式将空间数据包含在内。 图 3展示了这种方法的架构。
图 3. 用 BLOB 复制空间数据
Apply 进程对来自源视图的数据进行查询。在这里,它看到的不是 Spatial 字段,而是 BLOB 字段,这个字段中存储的实际上正是由源表转换来的空间数据。另外几个字段 —— 关键字字段“k”和数据字段“dl” —— 的复制方法与常规的 DB2 复制相同,不需要进行特殊考虑。数据是通过 Apply 进程插入到目标视图中的,如图中的虚线所示,这个过程最终跨越了系统的边界。目标视图的定义与源视图相匹配,这意味着存储空间数据的数据类型依然是 BLOB。 除了目标视图之外,我们还定义了三个 INSTEAD OF 触发器,用于在 INSERT、UPDATE 或 DELETE 操作的过程中实现逆向转换。逆向转换的作用是根据 Apply 进程复制过来的二进制数据构造出空间数据。
完全更新(即将源表中的全部数据复制到目标表上)的过程中使用的也是同一套逻辑。基本的原则与差量更新(即只复制上一次 Apply 周期以来发生变化的数据)相同。对于差量更新来说,我们必须考虑变化数据表及变化视图(CD 表和 CD 视图)。CD 表是通过 Capture 进程来维护的,这个进程负责扫描 DB2 日志,提取出数据变化信息。然后将这些数据变化信息收集到 CD 表中。CD 视图的作用与其他的视图类似,是 CD 表中数据的一种不同的表现形式。这些视图是从实际需要进行复制的视图中派生出来的。 图 4展示了源数据库,包括 CD 表和 CD 视图的定义。
图 4. 源表、源视图与 CD 表、CD 视图
现在我们面临一种两难的境地。首先,Capture 进程不能理解空间数据类型,因此,它无法收集 CD 表中变化的数据。第二个问题的来由是上面谈到的那个将空间数据在源视图中转换为 LOB 数据的过程。但是只要您考虑到 DB2 和 DB2 复制的下列功能,上述问题将迎刃而解:
- 复制部分字段
从一张表复制数据并不要求将这张表中的所有数据都复制过去。复制的数据源可以是字段的子集,这样的话,我们可以完全忽略掉源数据表中的某些字段。这一特性可用于在注册源数据表的时候忽略掉存储空间数据的字段。因此,我们就不会因为 Capture 不支持空间数据类型而受到限制。
- 复制 LOB
Capture 程序永远也不会从 DB2 日志中捕获 LOB 数据所发生的变化。当某个 LOB 字段注册为需要复制时,创建 CD 表的时候就会包含一个“更新指示器”字段,Capture 进程负责在读取源表的 DB2 日志记录的同时维护这个字段。当某条日志记录指示出 LOB 字段进行过更新时,这个更新指示器字段就设置为“U”;否则设为 NULL。从 Apply 这方面看,当源表的某个订阅中包含 LOB 字段时,Apply 程序会在 CD 表中查找与之相匹配的更新指示器字段,来检测 LOB 字段是否曾经更新。CD 表中那个相匹配的更新指示器字段与源表中 LOB 字段具有相同的名字。如果 LOB 字段被更新,那么 Apply 程序就直接从源表中取得 LOB 数据,然后将这部分数据应用到目标表上。
在这种实现方式中,由于要通过视图中的二进制数据类型访问空间数据,因此对于 CD 视图也需要构建相同的逻辑。然而,Capture 进程并不知晓空间字段要按照 LOB 字段来处理。这就要求我们伪造一个更新指示器字段。我们向源表中增加另一个字段,并定义两个触发器,仿照 Capture 进程维护 LOB 字段的更新指示器的方式来维护这个字段。然后用 DB2 Replication 注册这个附加的字段,这样,对该字段所作的任何修改都能够被捕获并保存在 CD 表中。CD 表及 CD 视图便都可以包含这个更新指示器。
现在就要用到前面描述的功能了。如图 4 所示,更新指示器字段标记为“@”。我们通过编写触发器,实现当空间字段被修改时将更新指示器的值设置为“U”。更新指示器同时也是 CD 表的一部分。这样,CD 表就可以保存空间字段是否被修改的信息了。CD 视图定义为将 CD 表中除更新指示器外的全部字段原封不动地映射过来,而将更新指示器的名字修改为经过转换的空间字段。结果是,Apply 进程在源视图中找到一个 BLOB 字段,又在 CD 视图中找到一个与该 BLOB 字段的名字相匹配的更新指示器字段。非空间性的数据是从 CD 表中取出的。当进行 INSERT 操作或 UPDATE 操作时,空间数据是从源视图中的 BLOB 字段里得到的,同时,CD 表中的更新指示器设置为“U”,表示空间数据曾经被修改过。 图 5说明了上面所讨论的全部逻辑流程。
图 5. 空间数据复制概览
下面几节将更详细地解释把空间数据转换成 BLOB 的必要性,以及如何设置源表和目标表。随后我们还会给出一个例子,引导您在 Replication Center 中执行各种必要的操作,并帮助您完整地理解这种空间数据复制方法。
结构化类型的转换函数
现在,让我们来看一看空间数据是如何在数据库服务器和客户机之间传送的。空间数据的表现形式为结构化的数据类型。正如在其他任何一种编程语言一样,结构化数据无法实现原封不动的简单传送。我们需要对其进行线性化处理,或将其转换成某种形式的字节流。然后,再把这种字节流从客户机传送到服务器上,反之亦然。
DB2(以及 SQL 标准)使用所谓的“转换函数”(transform function)来执行线性化操作,将 DB2 内置的结构化数据转换成外部表现形式,也可以转换回来。这种外部表现形式是一种单一的基于内置数据类型的标量值。内置数据类型可以是 VARCHAR、INTEGER、BLOB 或 CLOB,或者是 DB2 中包含的任何其他数据类型。转换结果到底要求为何种数据类型,这取决于特定的转换过程。在 DB2 Spatial Extender 中,我们用 BLOB 代表空间数据的外部二进制表现形式,而用 CLOB 代表空间数据的外部文本表现形式。
为了在 DB2 客户机和服务器之间实现通信过程,我们需要两个不同的转换函数。一个函数用来将空间数据从 SQL 形式转换成外部数据格式,另一个函数则将外部数据格式转换成 SQL 形式。为了简化这两个转换函数的使用过程,您可以采用“转换组”(transform group)。转换组中包含一个 FROM SQL 函数和一个 TO SQL 函数,前者负责将结构化数据转换成外部格式,后者负责根据用外部格式表示的数据构造出 SQL 数据库中的结构化数值。
转换组 下面让我们通过一个例子,看看转换函数和转换组的概念,以及它们的应用情况。DB2 Spatial Extender 提供了四个转换组。每一个转换组都包含一个将几何图形转换成外部表现形式的函数和一个将外部表现形式转换成几何图形的函数。这四个转换组支持下面几种数据格式:
- ST_WellKnownText
在几何图形和相应的已知文本表现形式(well-known text,WKT)之间转换。WKT 以文本数据的形式保存在 CLOB 中,最大长度为 2GB。ST_AsText 方法用于将几何图形转换为 WKT。
- ST_WellKnownBinary
在几何图形和相应的已知二进制(well-known binary,WKB)表现形式之间进行转换。WKB 以二进制数据的形式存储在 BLOB 中,最大长度为 2GB。ST_AsBinary 方法可用于将几何图形转换成 WKB。
- ST_GML
在几何图形和对应的地理标记语言(Geography Markup Language,GML)之间进行转换。GML 以文本数据的形式保存在一个 CLOB 字段中,最大长度为 2GB。ST_AsGML 方法可用于将几何图形转换成 GML。
- ST_Shape
在几何图形和对应的 ESRI Shape 表现形式之间进行转换。Shape 以二进制数据的形式存储在一个 BLOB 字段中,最大长度为 2GB。ST_AsShape 方法可用于将几何图形转换成 Shape。
从外部表现形式(WKT、WKB、GML、Shape)转换成相应的几何图形并存储在 SQL 数据库中的过程是由 ST_Geometry、ST_Point、ST_LineString、ST_Polygon、ST_GeomCollection、ST_MultiPoint、ST_MultiLineString 以及 ST_MultiPolygon 等构造函数实现的。每一个构造函数都经过重载,既可以接受 BLOB,也可以接受 CLOB 作为输入参数。根据输入数据的不同,这些构造函数可以决定您提供的到底是何种格式,然后对数据进行恰当的编码。
转换组的使用方法用 清单 1中的 SQL 代码就很容易解释明白。
清单 1. 使用转换组
CREATE TABLE t (
id INTEGER NOT NULL PRIMARY KEY,
g db2gse.ST_Point )@
INSERT
INTO t
VALUES ( 1, db2gse.ST_Point(10, 20, 0) ),
( 2, db2gse.ST_Point(110, 100, 0) )@
-- use the ST_WellKnownText transform group for bind-out
SET CURRENT DEFAULT TRANSFORM GROUP = ST_WellKnownText@
SELECT * FROM t;
ID G
----------- -------------------------------------------
1 POINT(10 20)
2 POINT(110 100)
2 record(s) selected.
DESCRIBE SELECT * FROM t@
SQLDA Information
sqldaid : SQLDA sqldabc: 896 sqln: 20 sqld: 2
Column Information
sqltype sqllen sqlname sqllonglen sqldatatype_name
----------- ------ ------- ---------- ------------------
496 INTEGER 4 ID 0 SYSIBM .INTEGER
409 CLOB 0 G 2147483647 DB2GSE .ST_POINT
-- use the ST_GML transform group for bind-out
SET CURRENT DEFAULT TRANSFORM GROUP = ST_GML@
SELECT * FROM t@
ID G
----------- -------------------------------------------
1 <gml:Point srsName="UNSPECIFIED"><gml:coord>
<gml:X>10</gml:X><gml:Y>20</gml:Y>
</gml:coord></gml:Point>
2 <gml:Point srsName="UNSPECIFIED"><gml:coord>
<gml:X>110</gml:X><gml:Y>100</gml:Y>
</gml:coord></gml:Point>
2 record(s) selected.
|
从上面的清单中可以看出,SELECT 语句取出表 T 中的所有字段。字段 G 声明的类型是 ST_Point,这是一种结构类型。很显然,当我们取出这些字段中存储的结构化数据时,需要对其进行转换。这个查询中并没有显式调用任何转换函数,但是 DB2 能够识别出这种结构化的数据,而且能够根据 CURRENT DEFAULT TRANSFORM GROUP 这一特殊注册项的设置来决定使用哪个转换组和转换函数。在第一个查询中,DB2 发现转换组指定为 ST_WellKnownText。该组中的 FROM SQL 转换函数是 ST_AsText()。这样,DB2 就调用 ST_AsText() 函数,将字段 G 中的所有结构化值转换成 CLOB。DESCRIBE 语句的输出能够证明这一点。当数据被取出的时候,这些 CLOB 值就和 ID 字段的值联系在了一起。
作为本节的结论,我们之所以要在空间数据中应用转换的概念,是因为我们想使从一个数据库向另一个数据库传输结构化数据的过程更加高效。然而,复制工具并不直接支持转换组和转换函数。因此,我们使用了一种更加手工化的方法。在视图的帮助下,我们将结构化数据转换为外部的表现形式,逆转换也是由视图和 INSTEAD OF 触发器一起实现的。下面两节将就这一点进行详细讨论。
捕获空间数据的变化
DB2 Replication 支持 LOB 的复制 1 ,空间数据也可以用转换组中的转换函数转换为 LOB。这些特性结合在一起,就能够直接支持空间数据的复制。在本节中,我们将解释如何对源表进行设置,以实现将空间数据转换为 LOB,并讨论 Capture 进程的功能。设置目标表的过程和 Apply 进程的功能将在下一节讨论。
转换组的概念通常用于在 DB2 数据库服务器和 DB2 客户机之间传送数据,在两个数据库之间传送数据也适用相同的概念。现在,源数据库变成了数据库客户机,它将数据提供给目标数据库(即数据库服务器)并存储在其中。从这个角度来看,这正是经典的客户机/服务器环境。
选择一种转换方式
前面已经讲过,我们在复制空间数据的时候可以选择不同的外部数据格式。为了确定正确的转换函数,我们需要制定下面的标准:
- 在源数据库和目标数据库上应该只需要很少的附加磁盘存储空间,或根本不需要。
- 转换过程应该尽可能快速执行,以避免对复制过程的整体性能产生影响。
为了同时支持这两项标准,我们既可以实现自己的转换组,也可以使用 DB2 Spatial Extender 已经提供的转换组。我选择使用已有的转换组 ST_WellKnownBinary,因为预定义转换组的功能经过了很好的测试,比起自己实现来可以降低成本。
选择文本形式还是二进制形式同样十分简单。文本形式通常比二进制形式更长,因为对于每一个浮点数而言,文本形式需要更多的字符才能表示。二进制数据的又一优点在于其更高的正确性。空间数据中的点是用符合 IEEE 754 标准的浮点数来存储的。将二进制数据用文本数据表示则牵扯到转换,会引入数据舍入的问题。因此,我们可能会降低已经存在于复制源中的数据的准确性,而且在目标数据库中重新构造二进制数据的过程又会进一步降低准确性。直接传输二进制数据就可以避免第二个问题。
根据前面的观察,转换组 ST_WellKnownBinary 和 ST_Shape 成为我们可能的选择。进一步观察 ESRI 的 shape 格式 [6] 可以发现,shape 格式无法识别几何图形是只由一个部分组成的具有多个部分的几何形状,比如 multipoint(10 10),还是单纯的只有一部分的几何形状,如 point(10 10)。因此,如果任何类型的几何图形存储在这张待复制的表中,那么特定的类型信息就可能在复制过程中丢失掉。然而,如果我们知道空间数据具有某种更加专用的类型,那么这个参数是不适用的。只有在目标表上使用适当的构造函数才能保证重新构造出正确的专用类型。
已知二进制格式对于大型系统而言还有一个优势。这种格式既支持小型编码也支持大型编码。而 shap 格式将所有的信息都存储在小型编码中(每一条记录的头部除外),因此,就需要将每一个浮点数从大型编码转换到小型编码。
在另一方面,已知二进制格式还有一个缺点,即它比 shape 格式稍微冗余一些。它用于表示几何图形所需的字节数要多出几个。因此,我们并不强制使用 ST_WellKnownBinary,而放弃 ST_Shape,因为这两个转换组都能够有效地实现相同的目标。
不过,DB2 的复制并不直接支持 DB2 的转换组机制。 它不允许用户在从源表取出数据或向目标表插入数据时定义某个组。 复制工具只能将空间字段识别为一种不支持的类型。结果是我们必须在复制过程开始处理空间数据之前手工将其转换。
这一限制的确带来一些不便,但并不能阻止我们复制空间数据。我们将基于源表定义一个视图,让从空间数据到外部格式的转换在这个视图中进行。以这种方式创建的视图现在可用作订阅组中的复制源。
在复制 LOB 数据的时候,LOB 不会存储在 CD 表或 CD 视图中。而是只有更新指示器存储于其中。更新指示器可以供 Apply 进程使用,用于确定是否对 LOB 字段执行了更新操作。如果该字段未被更新,那么就没有必要将 LOB 的值从源表拷贝到目标表中。这种方法可以避免不必要的数据移动。
请您注意,插入操作总是会引起 LOB 数据的拷贝,因为 LOB 数据是新记录的一部分,目标表中并不存在。出于我们将要在视图中实现的手工格式转换过程,CD 表中并不存在与源表对应的更新指示器。 前面提到,复制工具本身并不会把空间字段看成是一个可以复制的字段。结果导致我们必须自己提供并维护这样一个指示器字段。
视图的定义以及附加的更新指示器字段导致我们的方法分为四步,现在我们将概述其要点,并举出示例。假设我们打算复制 清单 2中定义的表中的所有数据,包括空间数据。
清单 2. 定义要复制的源表
CREATE TABLE streets (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
track db2gse.ST_LineString
)@
|
定义更新指示器
第一步是增加一个字段充当更新指示器,以及两个维护该字段的触发器。一个触发器用于在每次对记录进行更新时重置指示器。第二个触发器负责在当且仅当对几何图形字段进行更新时给指示器赋值。触发器是根据其定义的顺序触发的,也就是说,CREATE_TIME 较早的触发器会比在其之后定义的触发器先激活。由于我们希望先重置指示器,再在几何图形字段更新时对其赋值,因此我们必须当心触发器的执行顺序。必须先创建重置指示器的触发器,然后再创建更新指示器的触发器。 清单 3中的 SQL 语句显示了必要的修改操作,以及按正确的顺序定义触发器的情况。
清单 3. 为空间字段增加更新指示器
ALTER TABLE streets
ADD COLUMN track_update CHAR(1)@
CREATE TRIGGER streets_update NO CASCADE BEFORE
UPDATE ON streets
REFERENCING NEW AS n
FOR EACH ROW
MODE DB2SQL
SET n.track_update = NULL@
CREATE TRIGGER streets_track_upd NO CASCADE BEFORE
UPDATE OF track ON streets
REFERENCING NEW AS n
FOR EACH ROW
MODE DB2SQL
SET n.track_update = 'U'@
|
在源表上创建视图
为了让所有的数据都具有适合于复制的正确格式,我们需要在源表上定义一张视图。这张视图的定义将涉及源表中所有需要复制的字段。源表中有两个空间字段。如前所述,空间字段需要转换成已知二进制格式。ST_AsBinary 函数可用于实现这一转换。更新指示器是另一个未在视图定义中出现的字段。我们并不想复制更新指示器;这个字段只在 Apply 进程决定来自空间字段的数据是否需要复制的时候才是有用的。因此,我们用 清单 4中所示的 SQL 语句来创建基于复制源表的视图。
清单 4. 创建将空间数据转换成 BLOB 的视图
CREATE VIEW streets_repl_view(id, name, wkb) AS
SELECT t.id, t.name, db2gse.ST_AsBinary(t.track)
FROM stolze.streets AS t@
|
请注意,您在视图定义中应该总是用完全限定性的表名和对应的字段名,这样复制工具才能成功解析其定义,并析取出后面所需的相应信息。
创建视图和向源表中增加更新指示器的顺序无关紧要。这两个步骤都可以完全独立地进行。然而,后续步骤则要求必须完成前面这一步。
将源表注册为复制源
现在所有的准备工作都已完成,我们可以开始设置实际的复制环境了。当从视图进行复制时,DB2 复制机制要求将视图所基于的表也注册为复制源表。否则,我们就无法利用 Capture 进程来扫描日志文件,找出所有被修改且需要复制的记录。这意味着我们只能在目标数据库中对被复制的表进行完全刷新。而这在很多情况下显然不可行。
下面,我们假设您已经创建了捕获控制表(capture control table),也为数据库启用了复制功能。启用复制要求数据库配置参数 LOGRETAIN 设为 YES。请注意,您必须在修改了 LOGRETAIN 参数之后备份数据库。有关为数据库设置复制功能的更多细节信息,请参考 [4]。
注册源表的方法很简单。我们需要捕获除空间字段之外,且包括更新指示器在内的所有字段上的变化。 图 6显示在 DB2 Replication Center 中注册源表的情况。您从这张图中可以看到,TRACK 字段的任何变化都不会被捕获,因为它是具有结构化类型的字段。
图 6. 将表 STREETS 注册为复制源
注册过程创建了一张新的变化数据表(即 CD 表),Capture 进程用这张表记录从 DB2 日志中挖掘出来的 STREETS 表上发生的任何变化。让我们快速验证一下这一点。 清单 5显示了几条 SQL 语句,先对 STREETS 表中的数据进行了修改,然后查询 CDSTREETS 表,看是否已经捕获并组装好正确的数据。请您注意,可能要等待一段时间之后 Capture 进程才能从日志中取得数据填入 CD 表中。
清单 5. Capture 进程设置测试
INSERT
INTO streets(id, name, track)
VALUES (1, 'my first street',
db2gse.ST_LineString ('linestring(10 10, 20 20)', 1))@
SELECT id, VARCHAR(name, 20), wkb FROM cdstreets_repl_view@
ID 2 WKB
----------- -------------------- ---
1 my first street -
1 record(s) selected.
UPDATE streets
SET name = 'different street'
WHERE id = 1@
SELECT id, VARCHAR(name, 20), track_update FROM cdstreets@
ID 2 TRACK_UPDATE
----------- -------------------- ------------
1 my first street -
1 different street -
2 record(s) selected.
UPDATE streets
SET (id, track) = ( 2, db2gse.ST_LineString(
'linestring (5 10, 5 20)', 1) )
WHERE id = 1@
SELECT id, VARCHAR(name, 20), track_update FROM cdstreets@
ID 2 TRACK_UPDATE
----------- -------------------- ------------
1 my first street -
1 different street -
2 different street U
3 record(s) selected.
|
将视图注册为复制源
启动 capture 进程之前的最后一步操作是将视图 STREETS_REPL_VIEW 注册为复制源。然后,可将该视图用于订阅组中。这张视图中包含着以已知二进制格式编码的空间数据。
不幸的是,该视图并不能用 DB2 Replication Center 进行注册。复制工具会根据这张视图基于的所有表的 CD 表创建变化数据视图(CD 视图)。所有对该 CD 视图的引用必须找到对应的目标 CD 表。在我们的例子中,这就意味着如下的事实。CD 表中包含 ID、NAME 以及 TRACK_UPDATE 等字段。它并不包含 TRACK 字段,因为前面解释过,这个字段并不能注册。视图 STREET_REPL_VIEW 从表 STREETS 中引用 ID、NAME 和 TRACK 字段。相应的 CD 视图就必须引用 CD 表中相同的字段。 图 7描述了这些依赖关系。
图 7. 视图、表、CD 视图及 CD 表之间的依赖关系
复制工具分析 STREETS_REPL_VIEW 的视图定义,发现字段 TRACK 是 ST_AsBinary() 函数的输入。但是,CD 表中没有相应的字段 TRACK,因此,CD 视图无法创建。您将看到如 图 8所示的错误消息。
图 8. 将 STREETS_REPL_SOURCE 注册为复制源的出错消息
除了我已经说过的 CD 表中不存在 TRACK 字段的问题之外,我们还需要处理另外一个问题。最终得到的 CD 视图不应该包含 BLOB 字段(这个字段是 ST_AsBinary(TRACK) 这个函数调用的结果),而是应该包含更新指示器字段 TRACK_UPDATE。更新指示器已经存在于 CD 表中,所以这个依赖关系可以很轻易地满足。
为了按照要求创建 CD 视图,我们现在要使用一个哑视图,它的基础表是 STREETS。 创建这张视图时应该包括 TRACK_UPDATE 字段,而不包括 TRACK 字段。而这个字段与 STREETS_REPL_VIEW 中经过转换以后的 TRACK 字段名字相同,即 WKB。 清单 6是相应的 CREATE VIEW 语句。
清单 6. 创建包含更新指示器的哑视图
CREATE VIEW spatial_replication_dummy(id, name, wkb) AS
SELECT t.id, t.name, t.track_update
FROM stolze.streets AS t@
|
如 图 9所示,注册这张视图的方法很简单。在注册过程中,我们也要给这张 CD 视图取一个更加恰当的名字。
图 9. 注册哑视图
用这种方法生成 SQL 语句非常简单,不会产生任何错误或者警告消息。您也可以就这样执行这些 SQL 语句。但是这个哑视图并不是我们实际想要复制的视图。因此,必须在复制目录表中修正该视图的名称。目录表 IBMSNAP_REGISTER 中的 SOURCE_OWNER 和 SOURCE_TABLE 包含了完全限定的表名。我们需要对这部分数据进行调整,令其指向视图 STREETS_REPL_VIEW。 清单 7显示了当哑视图注册为复制源之后 IBMSNAP_REGISTER 表中的内容。
清单 7. IBMSNAP_REGISTER 目录表中的内容
SELECT source_owner, source_table FROM asn.ibmsnap_register@
SOURCE_OWNER SOURCE_TABLE
--------------- -----------------------------
STOLZE SPATIAL_REPLICATION_DUMMY
STOLZE STREETS
|
对这一项进行修改也是很简单的。您使用一条 UPDATE 语句就能实现必要的修正工作。当您修改完成之后,您可以将这张哑视图删除,因为已经不再需要它了。 清单 8阐明了这两个步骤。
清单 8. 修正复制目录信息并清除哑视图
UPDATE asn.ibmsnap_register
SET ( source_owner, source_table ) =
( 'STOLZE', 'STREETS_REPL_VIEW' )
WHERE source_table = 'SPATIAL_REPLICATION_DUMMY'@
DROP VIEW spatial_replication_dummy@
|
最后一步,您可以在 DB2 Replication Center 中双击正确的视图注册项。选中“Registered Views”,您就可以看到 STREETS_REPL_VIEW,如 图 10所示。
图 10. 验证已经注册的视图
成功注册视图之后,您现在可以定义那些将视图作为复制源的订阅成员了,数据可以通过视图复制到不同数据库的目标表中。对目标表的设置将在下面几节中详细解释。
准备目标数据库
当我们将复制源映射到目标上时,目标通常是一张基表。但是在有些情况下可以用视图代替,空间数据复制正是这样的情况。如果使用的是目标视图,那么目标表和基于这张目标表的目标视图必须预先定义好,这样才能用 Replication Center 定义映射关系。过去,目标视图通常用于合并、联合或外联接的情况。现在,由于 DB2 UDB v8.1 中提供了 INSTEAD OF 触发器,使目标视图具备了全新的应用潜能。这种触发器可以在对视图发出 INSERT、UPDATE 或 DELETE 操作时引发不同的处理逻辑。因此,目标视图与这样的 INSTEAD OF 触发器相结合通常情况下能够在 Apply 进程处理过程中扩展数据传输的可能性。
我们已经有了一张源视图,它执行从空间数据到外部数据格式的转换,也将这张视图注册为复制源,现在,我们可以在订阅成员中使用这张视图。空间数据转换成的 BLOB 并不受 DB2 Spatial Extender 的直接支持。而且 BLOB 并不是 ST_Geometry 值,因此不能在 Extender 所定义的各种不同的空间函数中作为输入参数。首先,我们必须执行逆向转换操作,将 BLOB 转换成几何图形类型。本节将一步步地解释如何设置目标数据库。
概述
由于空间数据的存在,我们必须对目标表做一些额外的处理。再次强调,复制工具不认识空间数据类型(或其他任何结构化类型),也不知道我们用已知二进制形式传输空间数据。我们必须实现一种机制,将二进制数据及时(即在 Apply 进程向目标表插入数据的时候)转换成相应的几何图形。
有了 DB2 UD v8.1 及其后续版本中的 INSTEAD OF 触发器,我们可以实现一种非常简单的解决方案。我们先自己创建目标表,在其上定义视图,并为这张视图提供三个 INSTEAD OF 触发器,用于执行将外部数据格式转换成几何图形,并插入目标表中的工作。 图 11更详细地解释了这个过程。请您注意,这个额外的过程使我们不能使用由复制工具生成的目标表。因此我们必须自己创建这些表。
图 11. 在目标数据库上的处理过程
数据库上的处理过程" src="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0402stolze/fig-11-234x264.jpg" width="234" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/"/>
创建基本表及视图
第一步是创建基本表,并在基本表上创建视图与 INSTEAD OF 触发器。基本表和视图的定义很简单,可以从源数据库直接拷贝过来。但是我们不需要在目标表中增加更新指示器字段。 清单 9重复了这些步骤。如果目标表在另一个数据库或另一个模式下的话,我们可以选择相同的表名与视图名。不过在本例中我用了一个前缀“TG_”,这样能更清楚地表明我引用的是哪一张表。
清单 9. 创建目标表,并在其上创建视图
CREATE TABLE tg_streets (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
track db2gse.ST_LineString
)@
CREATE VIEW tg_streets_repl_view(id, name, wkb) AS
SELECT t.id, t.name, db2gse.ST_AsBinary(t.track)
FROM stolze.tg_streets AS t@
|
准备好表与视图之后,您可以创建三个 INSTEAD OF 触发器。一个触发器负责在把新的数据从数据源复制到目标表上时发生的 INSTERT 操作。第二个触发器处理 UPDATE,当数据同时存在于源表与目标表,而在源表上对其进行修改时,这个触发器就派上用场了。当在源表的记录上执行了 DELETE 操作时,第三个触发器负责从目标表中删除相应的数据。 清单 10显示了在视图 TG_STREETS_REPL_VIEW 上定义的这三个视图。
清单 10. 在目标表的视图上定义 INSTEAD OF 触发器
CREATE TRIGGER tg_streets_repl_i INSTEAD OF
INSERT ON tg_streets_repl_view
REFERENCING NEW AS n
FOR EACH ROW
MODE DB2SQL
INSERT
INTO tg_streets(id, name, track)
VALUES ( n.id, n.name, db2gse.ST_LineString(n.wkb))@
CREATE TRIGGER tg_streets_repl_u INSTEAD OF
UPDATE ON tg_streets_repl_view
REFERENCING NEW AS n OLD AS o
FOR EACH ROW
MODE DB2SQL
UPDATE tg_streets
SET ( id, name, track ) = ( n.id, n.name,
db2gse.ST_LineString(n.wkb, 1) )
WHERE id = o.id@
CREATE TRIGGER tg_streets_repl_d INSTEAD OF
DELETE ON tg_streets_repl_view
REFERENCING OLD AS o
FOR EACH ROW
MODE DB2SQL
DELETE
FROM tg_streets
WHERE id = o.id@
|
现在,所有的设置都已经完成,您可以定义订阅集合(subscription set )及其成员了。
创建订阅集合
真正开始复制空间数据的第一个步骤是创建一个订阅集合(如果您还没有定义的话)。订阅集合通常是对若干张源表和目标表的映射进行分组。所谓映射是指定义从哪张源表将数据复制到哪张目标表。一个订阅集合中的所有映射会一起进行数据复制。您也可以定义若干个订阅集合,其中的每两个集合之间都是独立的。
在此,我们创建一个空白的订阅集合。后续步骤中我们向这个集合里加入订阅成员。 图 12显示了我们是如何定义一个名为 SPATIAL 的订阅集合的。capture server 名为 SPATIAL,这是源表所在的数据库,apply control server 名为 REPL,这是目标表所在的数据库。
图 12. 创建一个新的订阅集合
当然,您在创建订阅集合的时候就可以向其中加入部分或全部的成员。这可以通过“Create Subscription Set”对话框中的“Source-to-Target Mapping”面板来实现。我们现在不马上定义成员,而是用一个单独的步骤实现。用这种方法更容易实现反复增加新成员。
有关创建订阅集合的更多细节,请参考 DB2 Replication User's Guide and Reference 手册 [4]。
增加空间数据的订阅成员
设置好源表和目标表之后,我们现在就可以定义订阅成员了。这是空间数据复制过程中需要完成的最后一步管理任务。订阅成员的定义是小事一桩,我们用 DB2 Replication Center 来解释其中的每个操作步骤。
首先,打开订阅集合名的上下文菜单,选中其中的“Add Member...”选项,以打开“Add Member to Subscription Set”窗口。在“Memeber Information”面板上,加入已经注册的源视图 STREETS_REPL_VIEW。DB2 Replication Center 会为新的目标表产生一个名称。您必须修改这个名称,使之指向您已经根据实际的目标表创建好的视图,如 图 13所示。
图 13. 向订阅集合中增加成员
您还需要修正更多的细节信息,因此请选择 Details...按钮,在打开的窗口中接着进行处理。我们当然想将 WKB 字段的内容复制过来了。所以我们要把这个字段加入到已选择字段清单中,如 图 14 所示。 图 14中的第二幅图片还显示出已经选择的字段 WKB 是如何映射到同样名为 WKB 的目标字段上的。您可以从左边的蓝色按钮上拉出一个箭头,放在右边的红色按钮上,这样就在两项之间建立起关系来。
图 14. 将 WKB 字段加入到已选字段列表中
下一步中会指明 ID 字段上存在索引,这更大程度上是出于提示信息的目的。我们知道这样的索引是作为底层基本表 TG_STREETS 的主键的一部分出现的。要完成成员属性的设置就必须进行这一步。 图 15显示了其中的细节信息。
图 15. 为目标表定义索引
我们不想额外再定义记录过滤器,因此设置过程到此结束,点击“Member Properties”窗口和“Add Members to Subscription Sets”中的 OK按钮。所生成的语句分别在源数据库和目标数据库上执行,这样,成员的定义就完成了。
启动 Capture 和 Apply 控制服务之后,所有的空间数据现在都可以正确复制了。 清单 11中用一些小例子进行了验证。请注意,用常规文本表示的 SQL 语句集必须在源数据库中的源表 STREETS 上执行。以 粗体表示的查询语句从目标数据库的目标表 TG_STREETS 中获取数据。同时,在源数据库上的操作和数据向目标表传播之间存在延时。延时的长短取决于您在订阅集合中定义的时间表。语句在目标数据库上执行之后数据才复制成功,您必须等到这个时候才能看到如下所示的结果。
清单 11. 验证全部设置
INSERT
INTO streets(id, name, track)
VALUES ( 1, 'first street',
db2gse.ST_LineString('linestring(10 10, 20 20)', 1) )@
SELECT id, VARCHAR(name, 20),
VARCHAR(track..ST_AsText(), 50)
FROM tg_streets@
ID 2 3
----------- -------------------- ------------------------------------------------------------
1 first street LINESTRING(10.000000 10.000000, 20.000000 20.000000)
1 record(s) selected.
INSERT
INTO streets(id, name, track)
VALUES ( 2, 'second street',
db2gse.ST_LineString('linestring(10 15, 20 15)', 1) )@
SELECT id, VARCHAR(name, 20),
VARCHAR(track..ST_AsText(), 50)
FROM tg_streets@
ID 2 3
----------- -------------------- ------------------------------------------------------------
1 first street LINESTRING(10.000000 10.000000, 20.000000 20.000000)
2 second street LINESTRING(10.000000 15.000000, 20.000000 15.000000)
2 record(s) selected.
DELETE
FROM streets
WHERE id = 1@
SELECT id, VARCHAR(name, 20),
VARCHAR(track..ST_AsText(), 50)
FROM tg_streets@
ID 2 3
----------- -------------------- ------------------------------------------------------------
2 second street LINESTRING(10.000000 15.000000, 20.000000 15.000000)
1 record(s) selected.
UPDATE streets
SET name = 'another name'
WHERE id = 2@
SELECT id, VARCHAR(name, 20),
VARCHAR(track..ST_AsText(), 50)
FROM tg_streets@
ID 2 3
----------- -------------------- ------------------------------------------------------------
2 another name LINESTRING(10.000000 15.000000, 20.000000 15.000000)
1 record(s) selected.
UPDATE streets
SET (name, track) = ( 'yet another name',
db2gse.ST_LineString('linestring(5 5, 1 1)', 1) )
WHERE ID = 2@
SELECT id, VARCHAR(name, 20),
VARCHAR(track..ST_AsText(), 50)
FROM tg_streets@
ID 2 3
----------- -------------------- ------------------------------------------------------------
2 yet another name LINESTRING(5.000000 5.000000, 1.000000 1.000000)
1 record(s) selected.
|
上面的例子中并不包含任何针对 ID 字段的 UPDATE 操作。如果您希望对主键字段中的数据进行修改,或者,更确切地说,您希望对目标主键字段进行修改,那么您就需要先从定义目标主键的字段获取其先前的值。否则,Apply 进程就无法将在源表中修改的记录与相应的目标表记录匹配起来。有关这方面的更多细节请参考 DB2 Replication User's Guide and Reference [4]。
设置工具 db2se_repl
我们详细看一看 捕获空间数据的变化 和 准备目标数据库 两节中必要的设置步骤。为了更进一步简化管理任务,我们用 Java 编程语言实现了一个名为 db2se_repl 的工具 [7, 8]。您用这个工具可以通过完全限定名及其所在的数据库对源表进行详细说明,也可以定义您打算将数据复制到哪张目标表上。
语法
这个工具的定义是基于语法的,下面您可以看到这种语法的详细情况,以及若干选项和关键字的含义。图中的语法图遵从 DB2 SQL Reference所使用的样式。
>>--SETUP SPATIAL REPLICATION------------------------------>
>----| source |--| target |--+------------------------+---><
-'-VERBOSE--+---------+---'
-'-TRACE---'
source:
|--REPLICATE TABLE--+-----------------+--table_name-------->
-'-schema_name--.--'
>-----+---------------------------+------------------------>
| .--,------------. |
| V | |
-'-(-----column_name--+--)---'
>-----FROM CAPTURE SERVER--database_url-------------------->
>-----USER--user_name--PASSWORD--password------------------>
>-----+-----------------------------------------------+----|
-'-USING--(--CAPTURE SCHEMA--capture_schema--)---'
target:
|--TO TARGET TABLE--+-----------------+--table_name-------->
-'-schema_name--.--'
>-----+---------------------------+------------------------>
| .--,------------. |
| V | |
-'-(-----column_name--+--)---'
>-----ON APPLY SERVER--database_url------------------------>
>-----+---------------------------------------+------------>
-'-USER--user_name--PASSWORD--password---'
>-----USING--(--*---SUBSCRIPTION SET--set_name--*--,------->
>-----*--APPLY_QUALIFIER--apply_qualifier--*--,------------>
>-----*--SPATIAL REFERENCE SYSTEM IDENTIFIER--srs_id--*--)-|
|
REPLICATE TABLE schema_name.table_name ( column_name, & )
定义待复制的源表及其字段。如果未指定字段名,那么表中的所有字段都将被复制。在所有字段中必须有且仅有一个空间类型的字段。字段清单中必须包含源表的主键。
FROM CAPTURE SERVER database_url
定义可以找到源表的数据库的名称和位置。数据库的 URL 必须具备如下的格式: //host-name:port/database-name 这项信息用于通过 Type 4 JDBC 驱动程序链接源数据库。这意味着您不必将数据库加入本地目录。
CAPTURE SCHEMA capture_schema
定义源数据库上的 capture 模式。如果这项参数未指定,那么默认模式名称为“ASN”。
TO TARGET TABLE schema_name.table_name (column_name, -')
用限定性名称表示要复制到的目标表。如果该表不存在,则在目标数据库中基于源表的定义创建一个。在这里不指定字段名;字段名是根据源表派生出来的。
如果目标表已经存在,则重用,并且如果有指定的字段名,那么这些字段名必须和已经存在的表中的字段名相等。
ON APPLY SERVER database_url
指定目标表所在的数据库的名称和位置。数据库的 URL 必须具备下面的格式: //host-name:port/database-name 这项信息用于通过 Type 4 JDBC 驱动程序连接目标数据库。这意味着您不必非将这个数据库记录在本地目录中不可。
SUBSCRIPTION SET set_name
指定一个订阅集合,源到目标的映射关系将作为新成员加入其中。
APPLY QUALIFIER apply_qualifier
指定用于该订阅集合的应用限定符。
SPATIAL REFERENCE SYSTEM IDENTIFIER srs_id
指定一个空间参照系的数字标识符。在将已知二进制数据转换成空间数据时将用到这个标识符。这个空间参照系必须已经存在于目标数据库上。 在这里使用的空间参照系(spatial reference system,SRS)最好和源数据库中的数据使用的 SRS 相同。 您必须至少保证源数据库和目标数据库使用的是相同的坐标系。否则,您的数据将表现在一个不同的坐标系中,本质上就会导致不同的数据。
USER user_name PASSWORD password
定义用于链接源数据库或者目标数据库的用户名及口令。如果不为目标数据库提供用户名和口令,那么给源数据库提供的目标和口令将用于目标数据库。
VERBOSE
将 asnclp 工具生成的所有消息都输出到标准输出设备上来。默认情况下这些消息是看不到的。
TRACE
打开 asnclp 工具的跟踪机制。跟踪信息将写入标准输出流。
处理逻辑
这一工具的处理逻辑遵从 捕获空间数据的变化 和 准备目标数据库 两节的描述。只有当源表尚未注册为复制表时才能对其进行设置。如果注册已经完成,系统会简单地重用以前的定义。如果不是这种情况,那么工具会向源表中加入更新指示器字段、创建源视图,以及注册源表和源视图,从而创建 CD 表与 CD 视图。
在目标系统一方,工具也允许目标表已经存在。但是工具不会验证其字段和数据类型是否(与源表)匹配。如果目标表不存在,就创建一张。在这个阶段中我们也把源表的主关键字定义应用到目标表上。这个主关键字对于 INSTEAD OF 触发器的正常工作而言是必不可少的。带触发器的目标视图在订阅成员定义之前创建。
工具用于创建视图和触发器的模式名为 DB2SE_REPL。请您注意,视图的非限定性名称与表的非限定性名称相同。这意味着用这种方法设置的表都必须具备惟一的非限定性名称;也就是说,如果您的两个不同模式中都有名为 TAB 的表,那么 db2se_repl 工具将无法成功为这两张表设置复制。
复制专用的所有命令都是用 asnclp 工具 [5] 执行的。因此,这个工具并没有在对空间数据的处理之上又实现什么特殊的逻辑。请注意,并不是 asnclp 的所有可能选项都被传递到 db2se_repl 的接口上。我们致力于实现一个简单的接口,它不具备丰富的特性。比如说,如果您想要调整订阅集合的时间安排,或改变其类型(默认条件下定义的是只读的订阅集合),那么您应该要么自己创建订阅集合,那么在 db2se_repl 创建好之后到 DB2 Replication Center 中对其进行适当的修改。
示例输出
捕获空间数据的变化 和 准备目标数据库这两节使用表 STREETS 进行复制。工具也能实现上面描述的相同步骤, 清单 12 向您展示了这个过程是如何完成的。在我们的例子中,我们用名为 src_DB 的数据库作为源数据库,目标数据库是 TGT_DB。表 STREETS 放置在源数据库的模式 S 和目标数据库的模式 T 中。输出信息为您简短地描述了设置的过程。
清单 12. 用 db2se_repl 设置 STREETS 的复制
db2se_repl "SETUP SPATIAL REPLICATION
REPLICATE TABLE s.streets(id, name, track)
FROM CAPTURE SERVER //localhost:50000/src_db
USER db2admin PASSWORD ibmdb2
TO TARGET TABLE t.streets
ON APPLY SERVER //localhost:50000/tgt_db
USER db2admin PASSWORD ibmdb2
USING ( SUBSCRIPTION SET sp_set, APPLY QUALIFIER
apply_spatial, SRS ID 1 )"
Connecting to databases and checking setup...done.
Collecting defaults from database...done.
Extending source table with indicator column...done.
Creating replication view 'DB2SE_REPL.STREETS'...done.
Creating replication dummy view...done.
Registering source table and dummy view for replication...done.
Updating replication catalog...done.
Dropping dummy view...done.
Creating target table...done.
Creating replication view 'DB2SE_REPL.STREETS'...done.
Creating triggers on target view...done.
Creating subscription set 'SP_SET'...done.
Adding target view to subscription set 'SP_SET'...done.
Disconnecting from databases...done.
SER0000I The operation was completed successfully.
|
当成功执行设置之后,您可以启动 capture 和 apply 服务器,对源表 S.STREETS 中的空间数据或其他数据所作的所有修改都会被捕获到,并应用到目标表 T.STREETS 或者您定义的其他任何目标数据库上。您现在可以运行清单 11 中列出的那些测试,验证一下空间数据复制的设置是否能正常工作。
说明
上面描述的复制空间数据的方法并没有提到 DB2 Spatial Extender 或 DB2 Replication 中所有各种不同的细节情况。现在让我们来看看其中的几个问题。
并非复制问题 —— 地理编码 (geocoding)
有一个非常重要的问题需要谈一下,您可能并不一定在所有的情况下都用上面描述的方法来复制空间数据。如果您的源表或目标表上具备地理编码器(geocoder)(请参看 我以前的文章, [3])那么您可以从表中的其他字段,如地址字段等,再次生成空间数据。您可以对地址字段进行复制,这样就具备了对地址进行地理编码的全部必要信息,又可以生成相应的空间数据值。
根据地址数据生成空间数据的过程称为地理编码。地理编码器就是实现了地理编码算法的一个函数。 DB2 Spatial Extender User's Guide and Reference 中提供了一种架构,可以支持很多种不同的地理编码器。您也可以编写您自己的用户定义函数(user-defined function,UDF),将地址数据作为输入参数,将其转换成空间数据。地址数据可以是任何形式的非空间数据;它并不一定非得是“555 Bailey Ave. San Jose, CA 95141”这种真实世界中的地址。
如果您把地理编码器和您的复制方案结合起来使用,以便不复制空间数据自身,那么您就会使用到如 图 16 所示的一种设置。源表上有一个地理编码器,它根据同一张表中的地址数据生成空间数据 (1)。源表中包含着空间数据,访问源表的应用程序也可以使用这些空间数据。当把源表中的信息复制到目标表中时,您将复制包含地址数据的字段,但不复制空间数据字段 (2)。在目标数据库中,您再次使用地理编码器将地址数据转换成空间数据 (3)。地理编码必然在每一行数据上都发生两次:一次在源表上,另一次在目标表上。 图 16总结了上面描述的这种设置。空间字段由蓝色标出,在源表上的地理编码、复制、在目标表上对地址数据的地理编码这三个不同的步骤已经被编上数字。
图 16. 对复制得到的地址数据进行地理编码
在很多情况下,我们可能并不希望在目标表上对地址数据进行额外的地理编码操作。比如说,如果您使用了一个地理编码器,您就必须接受两次运行地理编码器带来的性能损失。另一个问题应该是,地理编码器可能不能对所有的地址进行编码。您可以为某些地理编码器不能处理的地址手工设置坐标。这种手工操作必须在复制数据上重复进行。此外,如果您使用 [3] 中描述的 Web 服务,那么很可能对地理编码器本身的使用也是强制收费的。对同一地址在复制之后再次进行地理编码,这样会增加单个地址的处理费用。在这种情况下,本文中介绍的技术对您来说也许是一种可行的选择,当然它对管理和设置的要求也是较高的。
请注意,如果您采用附加字段和触发器将空间数据转换成 LOB,类似地理编码器的方法也还是可以用的。触发器将重新产生附加字段的值(让我们称其为 SPATIAL_LOB 字段)。在对源表中的数据进行复制时,您也复制了 SPATIAL_LOB 字段中的数据。在目标表上,您将使用相同的方法,把复制过来的数据存储在一个单独的字段中。您可以再次使用触发器从复制过来的 LOB 生成空间数据。因为重新构造数据的时候使用的是空间函数,您可以简单地认为这些函数就是“地理编码器”,我们又一次面临上面已经解释过的情况。这种方法的缺点显然是要占用额外的磁盘空间,以及需要在源表的用户事务上执行到 LOB 的转换。我们提出的技术仅仅要求使用最小的磁盘空间(需要增加一个 CHAR(1) 字段),转换的工作量完全是由 Apply 进程处理的。
空间字段的 INLINE LENGTH
INLINE LENGTH 这方面的问题本文一直都没有提及。当创建表的时候,您应该考虑空间字段的 INLINE LENGTH 参数的定义。INLINE LENGTH 定义了结构化类型最大占据多少字节,在这个范围内结构化类型还是像 VARCHAR FOR BIT DATA 那样与一条记录的其他数据存储在一起。如果结构化的数值过长,就要像 BLOB 那样存储在一个与表相关联的长表空间中。INLINE LENGTH 越大,能与表中其余数据一起存储的(空间)数据就越多。
像 VARCHAR FOR BIT DATA 那样存储结构化数据的好处显然在于,这样的值可以缓存在 DB2 的缓冲区池中,而 LOB 是直接从磁盘上读写的,I/O 量较大。
复制中的可选项
DB2 复制还提供了很多其他的选项,可以用来对复制机制进行控制。举例来说,您可以改变时间表,在复制过程中的某些特定点上运行额外的 SQL 语句,对订阅集合进行分组并指派不同的限定符,创建索引、过滤记录、指定表空间,等等。所有这些选项大都和本文描述的空间数据复制方法是正交关系。有关这些选项的更多信息请参考 DB2 Replication User's Guide and Reference。
复制 LOB
空间数据转换成 LOB、对 LOB 进行复制、然后又在目标表上转换回空间数据。这种借用 LOB 的方法意味着复制 LOB 时需要考虑的特殊问题在复制空间数据时同样需要考虑。比如说,Apply 进程会从源表中选择最新版本的空间数据。这个版本可能和 CD 表中获得的记录中的其他数据并不是同一版本,这种不一致的情况在下一次 Apply 周期中将会得到修正,但是您应该了解这种可能性。
结束语
本文向您展示了一种利用 DB2 UDB V8.1 和 DB2 Replication 复制空间数据的方法。我们遵从已有的技术,用一张基于源表的视图将空间数据转换成外部数据格式(在我们的例子中是二进制)。相应的 CD 表和 CD 视图都需要作些许调整才能适应我们的需要。对源表进行适当的设置之后,Apply 进程就能以 BLOB 的形式捕获到空间数据发生的所有变化,并将其应用到目标视图上。再加上三个 INSTEAD OF 触发器,这张视图就相当于给 Apply 进程定义了一个操作的接口。触发器定义了逆向转换过程,从而再次构造出与源表中一样的空间数据。这一过程得到的结果是空间数据的一种无缝复制。我们详细地描述了如何设置源表和视图,以及目标表及其视图和触发器。
为了进一步简化设置的任务,您可以使用 db2se_repl。这个工具能执行必要的 SQL 语句和复制命令,可用来创建表、视图及触发器,并可通过 asnclp 命令行接口发出必要的复制命令、执行复制功能。 |