有捕获,有传播,接下来应该就是应用了,真巧,ORACLE也是这么想的。在Streams复制环境中,共享对象的修改正是按照这样的逻辑被捕获->传播->应用。你可以在目标端配置一个或多个apply进程应用这些修改,下面我们再通过一些文字描述一下这个流程。
Apply 进程也是oracle的后台进程,专门出队并应用LCRs地干活。应用LCRs是由专门的apply user负责,apply user即可以通过自定义规则执行应用,也可以通过apply handler。
1. LCRs 应用选项
Apply 进程提供了非常灵活的机制处理LCRs,因此你可能需要针对不同配置情况做不同的选择,比如前面在讲传播时就曾提到的,单个apply进程要么应用捕获的LCR,要么应用user-enqueued LCR,而不能同时应用二者(如果某队列同时包含两种LCR的话,则应用端至少需要两个apply进程),你可以使用DBMS_STREAMS_ADM/DBMS_APPLY_ADM包创建apply进程应用捕获LCRs,如果要应用user-enqueued LCRs,则必须使用DBMS_APPLY_ADM.CREATE_APPLY过程创建应用进程。
下面分别介绍不同应用:
A 、自动应用(Direct Apply)
顾名思议即自动执行无须用户干预。Apply进程要么成功应用LCR中的修改到数据库对象,要么应用时出错,出错的话将由conflict handler或用户自定义的error handler接管处理。
如果conflict handler能够解决冲突问题,其要么应用LCR,要么放弃相关修改。如果error handler能够处理错误,则应用该LCR,否则apply进程将该事务及LCRs相关的所有事务统统置入一个错误队列,等待用户手工处理,后面我们会有例子演示。
B 、自定义应用(Custom Apply)
指Apply进程将lcr做为参数传送到用户自定义的过程中处理。用户自定义的处理row LCRs中DML语句的过程被称为DML handler,处理DDL LCRs中DDL语句的过程被称为DDL handler。一个apply进程可以有多个DML handlers,但是只能有一个DDL handler。
自定义应用非常灵活,你甚至可以为每个表的不同类型操作指定不同的DML handler处理。比如为hr.employees表的insert操作指定一个DML handler,为其update操作指定另一个DML handler。又比如你希望在某个目标库中跳过对hr.employees的delete操作,可以通过自定义DML handler的方式专门处理delete操作来实现这一目的。另外注意通常DML handler中不应有事务提交或回滚,除非是显式指定的savepoint。
另外对于DML/DDL handler,你可以指定一个precommit handler,该handler也是一个pl/sql实现的过程,其功能是从internal commit获取commit SCN。precommit handler能够以自定义的方式处理提交信息。
提示:
不要通过DML handlers/error handlers/自定义规则函数修改LONG,LONG RAW,nonassembled LOB列。
2. 依赖关系
下面分别描述apply进程应用过程中对关联项的处理
2.1. 应用时关联的事务如何处理。
Apply 进程的parallelism参数控制应用的并行度。比如当该参数设置为1的时候,则只有一个apply进程按照事务在源端顺序一步步执行,这种情况下其实不需要考虑关联事务的问题,因为此时相当于是在串联执行,即不会有冲突也不会堵塞。需要注意的是当parallelism不为1时,多个apply进程可能同时应用多个事务,这些事务之间可能是有依赖和关联的,比如说源端a事务在b事务之前提交,但是目前端应用时为了提高应用效率,可能a/b两事务分别被不同apply进程同时调用,这个时候调用b事务的apply进程必然会等待调用a事务的apply进程执行完成了,再应用b事务中的修改。Apply进程会自动判断事务间的关联性。
提示:parallelism参数可以通过DBMS_APPLY_ADM.SET_PARAMETER过程设置。
2.2. 应用 Row LCR 时的顺序
分几种情况:
- 单事务情况下Row LCR应用顺序与源端相同序。
- 有事务依赖的情况下Row LCR应用顺序可见上节:应用时关联的事务如何处理
- 如果apply进程的commit_serialization参数设置为full,则apply进程按照源端的执行顺序提交所有事务,不管事务是否有关联的Row LCRs。如果apply进程的commit_serialization参数设置为none,则apply进程有可能忽略依赖项直接应用,因此应用顺序有可能与源端不同。
提示:commit_serialization参数也可以通过DBMS_APPLY_ADM.SET_PARAMETER过程设置。
2.3. 约束的处理
如果要更新的对象在目标端存在同名对象并且schema也相同,并且目标端对象定义了约束,apply进程会自动检测其在row LCRs中的依赖关系。
不管commit_serialization和parallelism参数值如何设置,apply进程总会优先考虑对象约束问题。比如apply进程应用一个事务时发现该事务包含的row LCRs依赖于另一个事务的row LCRs,则apply进程必须保证row LCRs以正确的顺序提交。
2.4. 定义虚拟依赖(Virtual Dependency Definitions)
某些情况下,apply进程需要一些附加信息来检查并行应用情况下row LCRs中对象的关联情况,比如:
- Apply 进程在目标端数据字典中找不对要更新的对象,这很正常,因为streams非常灵活,你可以配置从源端的一个对象复制到目标端另一个非同名对象中,当然这种情况下应用时就会比较复杂,比如说触发前面说的apply进程找不着要更新对象的问题。
- 数据未正确格式化,比如说单条row LCR中的sql语句实际触发更新多个表的数据。
如果apply进程未检查正确检测到依赖关系的话,应用错误或错误处理也可能执行。某些情况下上述的两种情况可以通过rule-based转换避免。比如说针对要更新的对象在源端和目标端并不存在于相同schema的情况,rule-based转换也能够在应用前修改schema。Rule-based转换的短板在于其不能适用于并行处理的情况。下面,主角登场,大家鼓掌,它就是。。。。。
Virtual dependency definition ,其实就是专用于apply进程的对象依赖信息的描述,用来在目标端检查事务间的关联性。Virtual dependency definitions不是以约束形式存在于目标端数据库,而是通过DBMS_APPLY_ADM包指定。Virtual dependency definitions提供了apply进程正确应用LCRs所需的必要信息,可以定义如下两种类型:
- Value Dependency
Value dependency 定义了一个表的约束,用来描述一个或从外表之间列的关联关系。Value dependency是一个或多个列的集合,apply进程通过value dependency来检查这些列在row LCRs之间的关联。Value dependency还可以定义虚拟的外键,只不过与真正的外键约束不同的是,value dependency定义的虚拟外键甚至可以同时关联多个表。如果目标端列之间关联关系未被明确通过约束等方式指定的话,Value dependency就能派上用场了,value dependency就是用来描述这些关联关系的。 定义value dependency可以通过DBMS_APPLY_ADM.SET_VALUE_DEPENDENCY过程,后续章节中会有示例详细说明。 Value dependency 有限制条件,一个value dependency中定义的对象关联关系不能跨多个数据库,同时,value dependency定义的列在源端必须打开附加日志,无条件记录这些列的所有操作。
- Object Dependency
Object dependency 定义两个对象间的父子关联关系。Apply进程在所有lower CSCN(commit system change number)涉及的父对象提交后执行事务关联的子对象。Apply进程使用row LCR中的对象标识检查依赖,而不是通过列值。如果目标端数据库表之间没有通过约束等方式定义关联关系的话(value dependency是当列之间),Object dependency就能派上用场。 可以通过DBMS_APPLY_ADM.CREATE_OBJECT_DEPENDENCY过程在目标端创建对象依赖,使用DBMS_APPLY_ADM.DROP_OBJECT_DEPENDENCY过程删除对象依赖。
2.5. Barrier Transactions
如果apply进程不能识别LCR中要更新的对象(即不存在于目标端数据库数据字典,也没有定义VDD),则该事务所包含的row LCR需要等到相同lower CSCN值的其它所有事务提交后再被应用,这种事务就被称为Barrier transaction。其它CSCN值高于该事务的也不会被应用,直到Barrier transaction提交。
3. 应用DML修改
3.1. 限制条件
必须确保主键列在源端操作时都被记录到redolog中,有多种方法能够确保操作被记录到redolog,比如附加日志。目标端的复合唯一键或外键约束在源库端必须需要启用附加日志。单列唯一键或外键则不需要,不过,如果源端约束列有多个,你也必须创建附加日志。
3.2. Substitute Key Columns
应尽可能给所有会被捕获的表创建主键或唯一键,前面捕获的部分三思已经介绍过相关内容,到应用的环节某些理论同样适用,比如说在应用/检测冲突等等时,oracle必须能够识别唯一列的信息,不然应用也无从做起。
默认情况下,Streams通过主键来唯一记录,如果没有定义主键,则通过最小的非空唯一键来确定记录唯一。如果上述两者都没有,你可以在目标端通过指定substitute key来唯一记录。
可以通过DBMS_APPLY_ADM.SET_KEY_COLUMNS过程设置substitute主键列,与真正的主键不同,substitute key列可以包含空值,并且substitute key列在应用时拥有更高的优先级,不过需要注意,如果你在目标端为表创建的substitute key引用列在源端表中并非是主键,那么务必在源端为该表启用附加日志。如果表即无主键也无唯一键,并且也未创建substitute key,那么apply进程会将该表除lob,long,long raw类型列除外的所有列组合在一起做为关键列,并且源端也必须为该表启用附加日志。
提示:oracle建议做为substitute key的列最好为非空列,并且为substitute key引用列创建索引以提高性能。
3.3. 列有差异时的应用
列差异是说源端和目标端相同表中列定义不同(可能是类型/长度不同,也可能是列名不存在),如果说streams复制环境中存在这种情况,一般可以通过rule-based转换或者DML handler处理器,下列各项描述的apply进程遇到列差异时的处理行为。
3.3.1. 列在目标端不存在
如果要更新的列在目标端不存在,则apply进程抛出异常并移动该事务到error queue。这种问题可以通过创建rule-based转换或者配置DML handler,通过DELETE_COLUMN过程在应用前将列所在LCRs中不存在的列删除。
3.3.2. 列在源端不存在
如果目标端的表拥有源端不存在的列,apply进程要视多出的列是否影响应用来决定其操作。如果额外列不影响应用的依赖关系,则apply进程直接应用修改。对于insert的操作如果额外列如果有默认值的话则插入默认值,否则该列值为null。
如果额外列影响应用,则apply进程也会将该事务置入error queue。
3.3.3. 列类型不同
如果源端与目标端表中列的类型不同,apply进程应用时也会抛出错误并将事务置入error queue。要避免这种错误发生,也可以通过创建自定义的rule-based转换或DML handler处理。
3.4. 索引组织表的应用
Apply 进程只支持对不含下列ROWID,UROWID,User-defined types类型的索引组织表应用修改,如果含有上述类似,则应用时也会抛出异常。
3.5. 解决冲突
多源的streams复制环境中有冲突是正常的,例如,在源端某表的记录被更新,同时目标端同名表的这些记录也被更新,当修改被传播到各自目标端的时候,apply进程在应用前就必须明确究竟是应用修改,还是保留本地的数据。Streams会自动检测冲突,对于update冲突如果配置了conflict handler会尝试使用解决。Streams提供了多种冲突解决方案,允许你根据自己的规则进行定制。
3.6. Handler 处理Row LCR
DML handler/Error handler/Update conflict handler 都可以处理row LCRs,不过注意同一个表的同一个操作不能同时定义DML handler和Error handler,下面分别描述配置了不同handler时的处理情况:
3.6.1. 没有配置handler
如果没有配置任何handler,apply进程直接应用row LCR,如果成功则表被更新,如果失败则应用事务回滚并移动至错误队列。
3.6.2. Update conflict handler
这种情况下apply进程也是尝试直接应用row LCR,如果成功则表被更新,如果由于非更新冲突(比如非唯一冲突或delete冲突)造成应用失败,则该事务回滚并移动至错误队列。如果由于更新造成失败,则update conflict handler被调用,如果update conflict handler能够解决冲突问题,则apply进程按照你配置了冲突解决方案应用或丢弃LCR,并继续应用该事务中其它LCRs。如果update conflict handler不能解决问题,则事务同样回滚并移动至错误队列。
3.6.3. DML handler
这种情况下DML handler完全控制row LCR的处理。某些DML handler能够执行SQL操作或者运行EXECUTE member procedure。如果DML handler运行EXECUTE member procedure,则apply进程尝试应用row LCR。
提示:
什么是 EXECUTE Member Procedure , 即在默认用户下执行 LCR。使用该方式应用LCR时,其它尝试应用该LCR的apply进程统统被该Apply进程接管处理,这就是前面说的完全控制row LCR的处理。
如果DML handler执行的SQL操作失败,或者尝试运行EXECUTE member procedure失败,则DML handler尝试处理这种异常。如果DML handler没有报出异常,则apply进程就认为DML handler为row LCR执行了正确的操作,apply进程继续应用该事务中其它row LCR。
如果DML handler不能处理异常,则将错误抛出,该事务回滚并移动至错误队列。
3.6.4. DML handler + Update conflict handler
这种情况下与单独配置DML handler相似,区别只在于如果DML handler执行的SQL操作失败,或者尝试运行EXECUTE member procedure遇到非update conflict导致的失败,则DML handler尝试处理这种异常。如果DML handler没有报出异常,则apply进程就认为DML handler为row LCR执行了正确的操作,apply进程继续应用该事务中其它row LCR。
如果由于update冲突导致EXECUTE member procedure执行失败,视EXECUTE member procedure参数conflict_resolution参数值的设置,apply进程执行不同的操作:
- Conflict_resolution 参数值设置为true:update conflict handler被调用。如果update conflict handler能够处理冲突,并且其它操作也都成功执行,则apply进程继续应用其它row LCRs,如果update conflict handler不能解决冲突问题,则DML handler尝试处理器,如果DML handler没有报出异常,则apply进程就认为DML handler为row LCR执行了正确的操作,apply进程继续应用该事务中其它row LCR,如果DML handler不能处理异常,则将错误抛出,该事务回滚并移动至错误队列。
- Conflict_resolution 参数值设置为false:update conflict handler不会被调用,DML handler尝试处理这种异常。如果DML handler没有报出异常,则apply进程就认为DML handler为row LCR执行了正确的操作,apply进程继续应用该事务中其它row LCR,如果DML handler不能处理异常,则将错误抛出,该事务回滚并移动至错误队列。
3.6.5. Error handler
这种情况下由Error handler负责row LCR的处理,与DML handler类似,Error handler完全控制处理过程Error handler能够执行SQL操作或者运行EXECUTE member procedure。如果Error handler运行EXECUTE member procedure,则apply进程尝试应用row LCR。
如果Error handler执行的SQL操作失败,或者尝试运行EXECUTE member procedure失败,则Error handler尝试处理这种异常。如果Error handler没有报出异常,则apply进程就认为Error handler为row LCR执行了正确的操作,apply进程继续应用该事务中其它row LCR。
如果Error handler不能处理异常,则将错误抛出,该事务回滚并移动至错误队列。
3.6.6. Error handler + Update conflict handler
这种情况下调用哪种handler去处理取决于错误类型:
- 如果是非update冲突导致的错误,则调用Error handler处理,处理规则同前小节Error handler。
- 如果是update冲突导致的错误,则调用Update conflict handler处理。如果update conflict handler能够处理冲突,则apply进程继续应用其它row LCRs,这种情况下不会再调用Error handler。如果update conflict handler不能解决冲突问题,则Error handler尝试处理,如果DML handler没有报出异常,则apply进程就认为Error handler为row LCR执行了正确的操作,apply进程继续应用该事务中其它row LCR,如果DML handler不能处理异常,则将错误抛出,该事务回滚并移动至错误队列。
|