在设计分布式应用程序时,必须考虑可用性与性能。一般的解决方案是在客户机系统上包含数据存储。通常,由于资源有限,客户机需要一个轻量级数据存储。这种方法为异构数据存储之间的数据同步带来了挑战。这个问题的一种解决方案是采用基于 java 的方法,用 JDBC 和 SyncML 标准进行异构数据库复制。但是,首先要了解一点背景知识。
注意:本文假定您熟悉服务器端 java 技术,不需要明确说明就能自如地安装和设置软件。
复制是在两个环境之间复制全部或部分数据库的过程。为了保持一致,对源数据库所做的更改要传播到复制的数据库中。
复制可以是 单向 的,也可以是 双向 的。双向复制可能更困难一些,因为对任何数据库所做的更改都可能产生不一致的数据。当这些更改在两个数据库之间传播时,需要有一个策略来调解这些差异,以便维护一致性。
作为复制的基本需求,我们需要惟一地标识将要复制的每个数据单元。对于双向复制,还需要一个能够标识数据库之间对应数据单元的映射方案。
根据复制的需求,可以采用各种各样的方案。对于单向复制,主数据库可以要求为数据单元生成 ID。对于双向复制,必须根据应用程序的 ID 生成方案定义映射方案。ID 生成方案与映射方案可以是每个数据库使用的 ID 编号的相互排斥范围,在这个范围内,ID 编号是正好匹配的。更复杂的示例中可能包括 ID 生成服务或者特定于数据库的方案,在这种情况下,必须在数据库之间维护 ID 的映射。
复制过程中的下一步是找出哪些数据单元已经更改。在关系数据库中,可以用附加字段来记录数据单元的状态和状态变化的时间戳。或者,也可以使用触发器使部分变化检测过程自动化。附加到表上的插入/更新/删除触发器可以检测到对数据单元所做的更改,并在更改日志表中记录这些更改。这样就可以用变更记录表(change log table)确定在任何指定时间上对数据单元所做的更改。
复制的目标是产生公共数据集的多个一致的副本。为了实现这个目标,必须交换每个数据库中检测到的更改,并使它们一致。
在这里,您面临着一个设计上的挑战。您可以作出如下选择:
- 表示将交换的那些更改的数据格式。
- 传输机制,例如 HTTP、FTP、JMS 等,取决于应用程序的需求。
- 参与实体之间的交换协议。
- 数据单元之间的冲突检测和解决机制。
SyncML 定义了一个同步协议,使用 XML 消息来添加、删除和修改数据单元。它也允许交换安全信息,这样节点就可以执行身份验证。
Sync4J 是 SyncML 的开源 java 实现,它能够支持多种传输机制。Sync4J 也根据应用程序的需求,给出了冲突检测框架和解决策略。
本文将介绍一个简单的数据复制示例。这个示例将演示两个异构数据库之间的双向客户数据记录复制,每个数据库都包含一个结构类似的客户表。使用惟一的客户 ID 来标识被复制的每条记录。我们在表中添加了两列,以记录每条记录的状态和最后状态变化时间戳。对于这个示例,状态与时间戳这两列必须通过应用程序或手工进行填充。正如前面说过的,触发器是一种在变更记录表中自动填充更改数据的适当解决方案。但是,在这个例子中使用的数据库目前拥有的触发器能力还很有限,因此我们选择不用触发器来实现这项任务。而且,这个实例不包括双向复制的冲突解决策略。所以冲突会导致复制过程失败。
本文附带的下载文件 j-sync4j.zip 中包含 SQL 脚本,可用它来设置源数据库和目标数据库的客户表。Sync4J 服务器模块和复制客户使用的代码也包含在这个 zip 文件中。
图 1 显示的网络图表描述了节点、数据库以及它们的连接方式:
图 1. 网络图表
我们要配置在 Tomcat servlet 容器中运行 Sync4J 服务器的服务器端环境,还要用 Derby 作为源数据库。
此外,还要配置客户端环境,用 HSQLDB 作为目标数据库,在上面运行 Sync4J 客户程序。(在这个例子中,假设已经安装了 J2SE SDK 和 Ant。)
要运行示例应用程序,需要在服务器端安装三个开源组件:Tomcat、Sync4J 服务器和数据库引擎。
按以下步骤安装三个主要的服务器组件(请参阅 参考资料,以获得下载链接):
- 下载并展开 Tomcat 5.0.29。本文中用 TOMCAT 表示 Tomcat 的安装目录。
- 将环境变量 J2EE_HOME 设为 TOMCAT。
- 下载并展开 Sync4J Server 4.0.2。本文中用 SYNC4J 表示 Sync4J 服务器的安装目录。
- 下载并安装 Derby。本文中用 DERBY 表示 Derby 的安装目录。
接下来,需要配置示例代码。请单击本文顶部或底部的 Code 图标来下载 j-sync4j.zip。将该文件解压到包含本示例使用的客户端代码、服务器代码和 SQL 脚本的目录中。本文中用 TESTSYNC 表示这个目录。
设置 JDBC 驱动程序的类路径:将 db2jcc.jar 和 db2jcc_license_c.jar 分别从 DERBY/lib 复制到 TOMCAT/common/lib 和 SYNC4J/lib 中。
接下来,将目录 TESTSYNC/derby 移动到 SYNC4J/default/sql。
提供的脚本与 Sync4J 服务器下载一起提供的其他数据库脚本类似,但是要针对 Derby 对它们稍作修改。
要构建示例 Sync4J 服务器模块,请按以下步骤进行:
- 将 TESTSYNC/module/build.bat 中的
SYNCSERVER_DIR
设置为指向SYNC4J
。
- 运行 TESTSYNC/module/build.bat。
要配置 Sync4J,请按以下步骤进行:
- 在 Derby 中创建叫做 sync4jdb 的数据库。步骤 3 的代码中描述了 JDBC 设置,这些设置假设 DERBY 中有一个叫做 sync4jdb 的数据库,用户名为 sync4j,口令为 sync4j。
- 用 Derby 附带的 IJ 工具创建并设置这个数据库(有关 Derby 工具的信息,请参阅 参考资料)。
- 设置 the properties in
SYNC4J/install.properties
中的属性,如下所示(用 JDBC 设置中的实际路径代替 DERBY):
dbms=derby jdbc.classpath=DERBY/lib/db2jcc.jar;DERBY/lib/db2jcc_license_c.jar; jdbc.driver=com.ibm.db2.jcc.DB2Driver jdbc.url=jdbc:derby:net://localhost:1527/"DERBY/sync4jdb" jdbc.user=sync4j jdbc.password=sync4j modules-to-install=foundation-1.0,pdi-1.1,testsync-1.0
执行 DERBY/frameworks/NetworkServer/bin/startNetworkServer.bat,将 Derby 作为网络服务器来启动。
有关将 Derby 作为网络服务器启动的更多信息,请参阅 Derby 的手册(请参阅 参考资料)。
用 IJ 工具执行 TESTSYNC/create_table_server.sql
,在 Derby 数据库中创建表 customer1。
要为 Tomcat 服务器安装 Sync4J 服务器并设置 Sync4J 数据库,需要完成以下步骤:
- 从 SYNC4J 中运行以下命令:
SYNC4J/bin/install.cmd tomcat
。 - 当给出命令提示时,按 Y 来重新构建 testsync-1.0 模块的数据库。
从 SYNC4J 中运行 SYNC4J/bin/sync4j-tomcat.cmd
,然后启动 Tomcat 服务器。
请注意服务器端设置;现在可以安装客户机了。
需要在客户端安装数据库引擎 HSQLDB。然后安装并配置示例代码。
要设置客户机应用程序数据库,请按下以步骤进行(请参阅 参考资料,以获得下载链接。):
- 下载并解压 HSQL 数据库。本文中用 HSQLDB 作为 HSQL 数据库的安装目录。
- 将 HSQL 作为网络服务器启动,它带有一个叫做 test 的数据库。
- 运行 TESTSYNC/create_table_client.sql 在 HSQL 数据库中创建类似的表(customer2)。
- 将 HSQL JDBC 驱动程序 HSQLDB/lib/hsqldb.jar 复制到 TESTSYNC/client/lib。
- 在
TESTSYNC/client/config/spds/sources/testsync.properties
中设置客户机的 JDBC 属性,如下所示:
jdbcDriver=org.hsqldb.jdbcDriver urlConnection=jdbc:hsqldb:hsql://localhost/test
- 正确地设置用户名称和口令。
然后,运行 TESTSYNC/client/build.bat 来构建客户端应用程序。
向 Derby 数据库中的 customer1
表中插入记录。例如:
insert into customer1 (userid, password, cl_op_type, clo_op_time) values ('user1', 'pass1', 'N', CURRENT_TIMESTAMP); |
向 HSQL 数据库中的 customer2
表中插入记录(请确保 userid 的值与 customer1
中记录的值不同)。例如:
insert into customer2 (userid, password, cl_op_type, clo_op_time) values ('user2', 'pass2', 'N', CURTIME()); |
运行 TESTSYNC/client/output/testsync.bat。
customer1
表现在应当包含刚才插入 customer2
表中的记录,而 customer2
表应当包含来自 customer1
表的记录。
在本文中,我们讨论了在设计通用数据库复制解决方案时涉及的一些重要概念和问题。在设计复杂的分布式应用程序时,资源占用少的嵌入式 Java 数据库的可用为您提供了一个很好的选择,同时它还允许使用异构数据库。对于复制过程和相关问题有很好理解,有助于我们改进这类系统的设计。我们鼓励您扩展这个示例,以便用冲突解决策略来处理双向复制,并使用记录更改数据的自动记录。