MobiLink开发资源集之同步数据冲突处理

来源:岁月联盟 编辑:zhuzhu 时间:2007-08-03

步骤清单

 

要解决冲突问题,我们应首先理解何谓冲突。

 

在向统一数据库中上载行时可能会发生冲突。如果两个用户修改不同远程数据库中相同的行,则当这两行中的第二个到达 MobiLink 同步服务器时将出现冲突。在冲突发生时,您应定义一个过程以计算正确值,或至少使用日志文件记录冲突。

 

在同步的下载阶段,远程数据库中不会出现冲突。如果下载的行中包含一个新的主键,则该行的值将插入到新的一行。如果该主键与一个现有行的主键相匹配,则会更新该行中的值。

 

冲突与错误是两个不同的概念。冲突处理是应用程序应该包含的一部分。

 

只有在更新行的过程中才会检测到冲突。尝试更新自上一次同步后已更新或删除的行将会导致冲突。缺省情况下:

 

*如果在尝试插入一行时发现此行已经插入,则会导致错误。

 

*如果在尝试删除一行时发现此行已经删除,则此尝试删除的操作将被忽略。

 

这些是为方便操作而定义的内置行为。如果需要不同的行为,可以使用一个或多个上载事件自行实现。

 

我们在demo的前半部分展示了有冲突而未解决的情况。当两个不同的客户端分别将相同的数据进行不同更改后,再在不同时间上载,后上载的数据成了最终结果。这可能是我们不想要的情况。我们需要对冲突进行一定的检测、然后再根据我们的用户规则进行处理。

 

在 MobiLink 客户端向 MobiLink 同步服务器发送一个更新后的行时,发送的数据中不仅包含该行的新值(后映像),而且还将包含旧行值的副本(前映像)。如果前映像与统一数据库中的当前值不匹配,表示检测到了冲突。

 

 

检测冲突的方法有两种:

*定义一个 upload_fetch 脚本。

 

*定义一个 upload_update 脚本,该脚本在 WHERE 子句中包含所有非主键列。

 

如果为同一个表定义这两个脚本,则只使用 upload_fetch 脚本。

 

仅当使用 upload_fetch 脚本或适当的 upload_update 脚本时,MobiLink 同步服务器才检测冲突。如果提供了 upload_fetch 脚本,MobiLink 同步服务器会将上载的前映像与 upload_fetch 脚本返回的包含相同主键值的行值进行比较。如果存在前映像中的值与当前统一数据库中的值不匹配的情况,则表示 MobiLink 同步服务器检测到了冲突。

 

我们这里用upload_fetch:

 

CALL ml_add_table_script('remote1','upload_fetch','SELECT empno, empname, gender, deptno, last_modified FROM emp WHERE empno = ?')

出现冲突时,可以使用临时表(或永久表)和 resolve_conflict 脚本加以解决。

 

例如,我们可以先建两个临时表,然后在upload_old_row_insert表事件中把统一数据库的旧值插入专门存放旧值的临时表,然后再 upload_new_row_insert表事件中把想要更新的值插入专门存放新值的临时表。接下来就可以在resolve_conflict表事件中解决冲突了。

 

当然,除了通过resolve_conflict事件解决冲突,我们也可利用upload_update、end_upload来解决冲突。在本部分的屏幕录像中,我们先展示了没有自定义冲突处理时候MobiLink对冲突的默认解决方案,即后者决定的策略。在此之后,我们又自定义了新的MobiLink冲突解决方案,让部门号较小的更新成为最终值。

 

本部分的脚本如下:

 

先建立两个目录:remote1 & remote2。

 

把数据库分别拷贝。

 

启动个数据库,中心叫center,其余两个一个叫remote1,一个叫remote2。

 

重新建立remote1和remote2的ODBC

对3个库分别执行:

 

ALTER TABLE emp ADD last_modified TIMESTAMP DEFAULT TIMESTAMP dbisql:remote1:CREATE PUBLICATION Lab_05(TABLE emp)CREATE SYNCHRONIZATION USER "remote1"CREATE SYNCHRONIZATION SUBSCRIPTION TO Lab_05FOR "remote1"TYPE 'TCPIP'ADDRESS 'host=localhost;port=2439'OPTION ScriptVersion='remote1'  remote2:CREATE PUBLICATION Lab_05(TABLE emp)CREATE SYNCHRONIZATION USER "remote2"ALTER SYNCHRONIZATION SUBSCRIPTION TO Lab_05FOR "remote2"TYPE 'TCPIP'ADDRESS 'host=localhost;port=2439'OPTION ScriptVersion='remote1'center:CALL ml_add_table_script('remote1', 'emp', 'download_cursor', 'SELECT * FROM emp WHERE last_modified > ?'); CALL ml_add_table_script('remote1', 'emp', 'upload_update', 'UPDATE emp SET empname=?, gender=?, deptno=? WHERE empno=?');COMMIT

 

 

命令行下:

 

dbmlsrv9 -c "dsn=center" -v+ -x tcpip -dl -o mlserver.mls -zu+dbmlsync -c "dsn=remote1" -v -o dbmlsync1.out -e "sv=remote1"dbmlsync -c "dsn=remote2" -v -o dbmlsync2.out -e "sv=remote1"

 

对remote2:

 

 

UPDATE emp SET deptno = '0005' WHERE empno = 1COMMIT

再次同步remote2

 

 

dbmlsync -c "dsn=remote2" -v -o dbmlsync2.out -e "sv=remote1"

 

 

对remote1

 

UPDATE emp SET deptno = '0002' WHERE empno = 1COMMIT

 

 

 

 

同步remote1

 

dbmlsync -c "dsn=remote1" -v -o dbmlsync1.out -e "sv=remote1"

 

对center:

 

 

CREATE GLOBAL TEMPORARY TABLE emp_new(empno INTEGER NOT NULL PRIMARY KEY,empname VARCHAR(20),gender BIT,deptno CHAR(4),last_modified TIMESTAMP);CREATE GLOBAL TEMPORARY TABLE emp_old(empno INTEGER NOT NULL PRIMARY KEY,empname VARCHAR(20),gender BIT,deptno CHAR(4),last_modified TIMESTAMP)CALL ml_add_connection_script('remote1','end_upload','DROP TABLE emp_new;DROP TABLE emp_old');CALL ml_add_table_script('remote1','upload_fetch','SELECT empno, empname, gender, deptno, last_modified FROM emp WHERE empno = ?')CALL ml_add_table_script('remote1','emp','upload_old_row_insert','INSERT INTO emp_old VALUES(?,?,?,?)');CALL ml_add_table_script('remote1','emp','upload_new_row_insert','INSERT INTO emp_new VALUES(?,?,?,?)');CALL ml_add_table_script('remote1','emp','resolve_conflict','CALL ResolveConflictDemo()');COMMIT;ALTER OR REPLACE PROCEDURE ResolveConflictDemo()BEGINUPDATE emp eSET e.deptno = en.deptnoFROM emp_new enWHERE e.empno = en.empnoAND en.deptno < e.deptno;DELETE FROM emp_new;DELETE FROM emp_old;END;