第二十七章 Caché 使用对象同步功能

第二十七章 Caché 使用对象同步功能

本附录描述了对象同步功能,可以使用该功能来同步“偶尔连接”系统上的数据库中的特定表。

介绍对象同步

对象同步是Caché对象可用的一组工具,允许应用程序开发人员建立一种机制来同步“偶尔连接”的系统上的数据库。通过此过程,每个数据库都会更新其对象。对象同步为Caché系统工具提供了补充功能,可提供高可用性和shadowing。对象同步并非旨在为实时更新提供支持。相反,它对于需要以不连续的间隔进行更新的系统最为有用。

例如,典型的对象同步应用程序将处于这样的环境中,即中央服务器上有数据库的主副本,而客户端计算机上有辅助副本。考虑销售数据库的情况,其中每个销售代表在便携式计算机上都有该数据库的副本。当销售代表Mary不在现场时,她会更新数据库副本。当她将计算机连接到网络时,数据库的中央和远程副本将同步。这可以每小时,每天或任何间隔同步。

两个数据库之间的对象同步涉及使用彼此的数据更新每个数据库。Caché不支持双向同步。而是将来自一个数据库的更新发布到另一个数据库。然后以相反的方向发布更新。

对于典型的应用程序,如果有一个主数据库和一个或多个本地数据库(如先前的销售数据库示例中所示),建议先从本地数据库更新到主数据库,然后再从主数据库到当地的数据库进行更新。

对于对象同步,客户端和服务器是有协议的。对于任何两个数据库,都可以执行双向更新。如果有两个以上的数据库,则可以选择用于更新所有数据库的方案(例如,本地数据库与主数据库独立同步)。

GUID

为了确保更新正常工作,数据库中的每个对象都应该是唯一可区分的。为了提供此功能,Caché为每个单独的对象实例提供一个GUID(全局唯一ID)。 GUID使每个对象普遍具有唯一性。

根据GUIDENABLED参数的值,可以选择创建GUID。如果GUIDENABLED的值为1,则将GUID分配给每个新对象实例。

考虑以下示例。两个数据库是同步的,每个数据库中都有相同的对象集。同步后,每个数据库都添加了一个新对象。如果两个对象共享一个公共GUID,则对象同步会将它们视为处于两个不同状态的同一对象;否则,对象同步将它们视为同一对象。如果每个都有自己的GUID,则对象同步会将它们视为不同的对象。

更新如何工作

从一个数据库到另一个数据库的每次更新都作为一组事务发送。这样可以确保所有相互依赖的对象一起更新。每笔交易同步容取决于“源”数据库日记的内容。该更新可以包括一个或多个事务,最多可以包括自上次同步以来发生的所有事务。

解决以下条件是应用程序的责任:

  • 如果两个共享唯一密钥的实例具有不同的GUID。这需要确定两个记录是描述单个对象还是两个唯一对象。
  • 如果两个更改需要统一。这就需要确定这两个更改是针对公共属性还是对不相交的属性集。

SyncSet和SyncTime对象

当两个数据库要同步时,每个数据库中都有另一个数据库所缺少的事务。如下图所示:

两个不同步的数据库

此处,数据库A和数据库B已在数据库536的事务536和数据库B的事务112处同步。每个数据库的后续事务都需要彼此更新。为此,Caché使用了所谓的SyncSet对象。该对象包含用于更新数据库的事务列表。例如,当将数据库B与数据库A同步时,SyncSet对象的默认内容是事务547、555、562和569。类似地,当将数据库A与数据库B同步时,SyncSet对象的默认内容是事务117、124、130和136。(事务不使用连续的数字集,因为每个事务封装了多个插入,更新和删除,它们本身使用中间数字。)

每个数据库都保存着与另一个数据库的同步历史记录。该记录称为SyncTime表。对于数据库,其内容的形式为:

注意:与每笔同步相关的数字不提供任何形式的时间戳。相反,它们指示单个数据库中的事务提交顺序。

数据库B与数据库A同步后,两个数据库可能如下所示:

两个数据库,其中一个已与另一个同步

因为事务正在添加到数据库B中,所以它们会在该数据库中产生新的事务编号。

类似地,数据库B与数据库A的同步导致将117、124、130和136添加到数据库A(并在其中接收新的事务编号):

两个同步数据库

请注意,来自数据库B的,来自数据库A(140到162)的事务不会更新回数据库A。这是因为从B更新到A使用了一项特殊功能,该功能是同步功能的一部分。其工作方式如下:

  1. 数据库中的每个事务都标记有所谓的“来源数据库”。在该示例中,数据库B中的事务140将被标记为源自数据库A,而其事务136将被标记为自身(数据库B)。

  2. SyncSet.AddTransactions()方法捆绑了一组事务以进行同步,它使可以排除源自特定数据库的事务。因此,从B更新到A时,AddTransactions()排除所有源自数据库A的事务-因为已经将这些事务添加到数据库B的事务列表中。

此功能可防止创建无限循环,在无限循环中,两个数据库使用同一组事务连续更新。

修改类以支持同步

对象同步要求站点具有带有匹配GUID集的数据。如果从一个尚未为记录分配GUID的现有数据库开始,则需要为数据库中的每个实例(记录)分配一个GUID,然后确保每个站点上都有匹配的数据库副本。详细过程是:

  1. 对于每个要同步的类,将OBJJOURNAL参数的值设置为1。
Parameter OBJJOURNAL = 1;

这将激活每个事务中归档操作(即插入,更新或删除)的日志记录;此信息存储在^ OBJ.JournalT全局中。 OBJOURNAL值1指定在归档操作中更改的属性值存储在系统日志文件中;在同步期间,需要从该文件中检索需要同步的数据。

注意:“ ONJOURNAL”也可以具有2值,因为在特殊情况下只能使用此值。 永远不会对于使用默认存储机制(%CacheStorage)的类.值为2表示在归档操作中更改的属性值存储在^ OBJ.Journal全局变量中;在同步期间,需要从该全局区域检索需要同步的数据。另外,将信息存储在全局数据库中会很快增加数据库的大小。

  1. 对于每个要同步的类,将其GUIDENABLED参数的值设置为1;否则,将其值设置为1。这告诉Caché允许将类与GUID存储在一起。
Parameter GUIDENABLED = 1;

请注意,如果未设置此值,则同步将无法正常进行。另外,必须为串行类设置GUIDENABLED,但不能为嵌入式对象设置GUIDENABLED。
3. 重新编译该类。
4. 对于每个要同步的类,通过运行AssignGUID()方法为每个对象实例赋予其自己的GUID:

 Set Status = ##class(%Library.GUID).AssignGUID(className,displayOutput)
  • className是其实例正在接收GUID的类的名称,例如“ Sample.Person”。
  • displayOutput是一个整数,其中零表示不显示任何输出,非零值表示显示输出。

该方法返回%Status值,应该检查该值。
5. 将数据库副本放在每个站点上。

执行同步

本节介绍如何执行同步。提供更新的数据库称为源数据库。接收更新的数据库是目标数据库。要执行实际的同步,过程是:

  1. 每次希望同步两个数据库时,请转到具有源数据库的实例。在源数据库上,使用%SYNC.SyncSet类的%New()方法创建一个新的SyncSet:
Set SrcSyncSet = ##class(%SYNC.SyncSet).%New("unique_value")

%New()的整数参数unique_value应该是易于识别的唯一值。这样可以确保每个站点上对事务日志的每次添加都可以区分开。

  1. 调用SyncSet实例的AddTransactions()方法:
 Do SrcSyncSet.AddTransactions(FirstTransaction,LastTransaction,ExcludedDB)
  • FirstTransaction是要同步的第一个事务编号。
  • LastTransaction是要同步的最后一个事务号。
  • ExcludedDB在数据库中指定一个名称空间,该名称空间的事务未包含在SyncSet中。

此方法收集同步数据并将其放在全局中,以备导出。

或者,要同步自上次同步以来的所有事务,请省略第一个和第二个参数:

 Do SrcSyncSet.AddTransactions(,,ExcludedDB)

这将获取所有事务,从第一个未同步的事务开始到最近的事务。该方法使用SyncTime表中的信息来确定值。ExcludedDB是如下创建的$ LIST:

Set ExcludedDB = $ListBuild(GUID,namespace)
  • GUID是目标系统的系统GUID。该值可以通过%SYS.System.InstanceGUID类方法获得;要调用此方法,请使用## class(%SYS.System).InstanceGUID()语法。

  • 名称空间是目标系统上的名称空间。

  1. 调用ErrCount()方法来确定遇到了多少错误。如果存在错误,则SyncSet.Errors查询将提供更多详细信息。

  2. 使用ExportFile()方法将数据导出到本地文件:

Do SrcSyncSet.ExportFile(file,displaymode,bUpdate)
  • file是要将同步导出到的文件;它是具有相对或绝对路径的名称。
  • displaymode指定该方法是否将输出写入当前设备。将“ d”指定为输出,或将“ -d”指定为无输出。
  • bUpdate是一个布尔值,它指定是否更新SyncTime表(默认值为1,表示True)。此时将其显式设置为0,然后在源收到目标已确实收到数据并执行同步的保证后,将其设置为1可能会有所帮助。
  1. 将导出的文件从源计算机移动到目标计算机。
  2. 使用SyncSet。%New()方法在目标计算机上创建SyncSet对象。对%New()的参数使用与源计算机上相同的值-这就是标识同步事务的源的原因。
  3. 使用Import()方法将SyncSet对象读入目标计算机上的Caché实例:
 Set Status = TargetSyncSet.Import(file,lastSync,maxTS,displaymode,errorlog,diag)
  • file是包含要导入的数据的文件。
  • lastSync是最后一个同步的事务号(synctime表中的默认值)。
  • maxTS是SyncSet对象中的最后一个事务编号。
  • displaymode指定该方法是否将输出写入当前设备。将“ d”指定为输出,或将“ -d”指定为无输出。
  • errorlog提供任何错误信息的存储库(并通过引用进行调用以为应用程序提供信息)。
  • diag提供有关导入时发生情况的更详细的诊断信息

此方法将数据放入目标数据库。它的行为如下:

  1. 如果该方法自上次同步以来检测到该对象已在源数据库和目标数据库上进行了修改,则它将调用%ResolveConcurrencyConflict()回调方法;否则,将调用%ResolveConcurrencyConflict()回调方法。与其他回调方法一样,%ResolveConcurrencyConflict()的内容由用户提供。(请注意,如果两个更改都同时修改了一个公共属性,或者两个更改都分别修改了非相交的属性集,则会发生这种情况。)如果未实现%ResolveConcurrencyConflict()方法,则该冲突仍未解决。
  2. 如果在Import()方法执行后存在未成功解决的冲突,则这些冲突将作为未解决的项目保留在SyncSet对象中。确保对剩余的冲突采取适当的措施;这可能涉及解决方案,使项目处于未解决状态,等等。

如果此过程完成,则Import()方法将返回成功。

  1. 一旦第一个数据库更新了第二个数据库,请在另一个方向上执行相同的过程,以便第二个数据库可以更新第一个数据库。

注意:对象同步不支持文件流的同步。

在GUID和KID之间转换。

要从其GUID确定对象的OID,反之亦然,可以使用两种方法:

  • %GUIDFind()是%GUID类的类方法,该类方法采用对象实例的GUID并返回与该实例关联的OID。

  • %GUID()是%Persistent类的类方法,该类方法获取对象实例的OID并返回与该实例关联的GUID。仅当相应类的GUIDENABLED参数为TRUE时,才可以运行该方法。如果OID不包含该信息,则此方法以多态方式分派并确定最特定的类型类。如果实例没有GUID,则该方法返回一个空字符串。

手动更新SyncTime表

要在数据库的SyncTime表上执行手动更新,请调用SetlTrn()方法,该方法设置最后一个事务号:

Set Status=##class(%SYNC.SyncTime).SetlTrn(syncSYSID, syncNSID, ltrn)
  • syncSYSID是目标系统的系统GUID。该值可以通过%SYS.System.InstanceGUID类方法获得;要调用此方法,请使用## class(%SYS.System).InstanceGUID()语法。

  • syncNSID是目标系统上的名称空间,该名称空间保存在$ Namespace变量中。

  • ltrn是已知已导入的最高交易编号。可以通过调用SyncSet的GetLastTransaction()方法来获取此值。

SetlTrn()方法设置在目标系统上同步的最高同步号,而不是默认行为(即设置从源系统导出的最高同步号)。两种方法都不错,并且是在应用程序开发期间可用的一种选择。

发布了29 篇原创文章 · 获赞 29 · 访问量 2181

猜你喜欢

转载自blog.csdn.net/yaoxin521123/article/details/105379409
今日推荐