发表于: 2006.01.16 19:57
分类: 读书笔记
出处: http://yangtingkun.itpub.net/post/468/50770
---------------------------------------------------------------
本章主要描述将事件部署到队列中和将事件从一个队列传播到另一个队列的相关概念。
事件的部署和传播概述:
流使用SYS.AnyData类型的队列在部署事件。两种类型的事件可以部署到流的队列中:LCR和用户信息。
部署的事件可以被消费、传播或既消费又传播。应用进程或用户应用程序通过显示的出队列来消费事件。被消费的事件如果进行了消息保留的设置,或者在流机制中需要传播到其他队列中,那么事件会仍然保留在队列中。其他的队列可以和当前队列处于同一个数据库中,也可以处于不同的数据库中。不论哪种情况,把事件传播出去的队列被称为源队列,接收事件的队列被称为目的队列。源队列和目的队列之间可以是一对多、多对一或者多对多的关系。
传播可以创建、修改和删除,也可以定义传播的规则来控制哪些事件将被传播。源队列的拥有者进行事件的传播。用户必须具有下列权限才能进行传播:
传播使用的规则集的执行权限;
规则集中使用的所有转换函数的执行权限;
如果源和目的队列处于同一个数据库中,还需要具有对目的队列的入队列权限。
如果目的队列处于远端数据库,则连接远端数据库的数据库链的连接用户必须具有目的队列的入队列权限。
捕获的和用户加入队列的事件:
事件可以通过两种方式加入到队列中:
捕获进程将修改转化为LCR加入到队列中;这种包含LCR由捕获进程加入到队列中的事件又叫做捕获事件captured event;
用户应用程序加入的SYS.AnyData类型的用户信息。由用户显示加入或用户应用程序加入的用户事件叫做用户入队事件user-enqueued event。
事件出队列由两种方式:
应用进程将捕获事件或用户入队事件出队列;如果事件包含LCR,则应用进程或者直接执行这个LCR,或者调用用户指定的过程来处理。如果事件不包含LCR,则应用进程调用用户指定的过程(message handle)进行处理。
用户应用程序显示将用户入队事件出队列并处理。
在队列之间传播队列:
用户可以使用流来配置事件在两个队列间的传播。流使用JOB来进行传播的配置。
传播总是发生在远队列和目的队列之间。一个源队列可以传播事件到多个目的队列,一个目的队列可以接收到多个源队列的传播。但是一个源队列和一个目的队列之间只能配置一个传播。一个队列可以是某个传播的源队列同时是某个传播的目的队列。
传播可以将源队列中的所有事件传播到目的队列,也可以通过建立规律来限制传播哪些事件到目的队列。一个传播可以传播捕获事件和用户入队事件。
根据流环境的配置,修改可能会被送回到修改本身发生的数据库。用户应该保证配置了相应的策略来避免这种无限循环的产生。通过使用流标志(streams tag)可以避免循环修改的发生。
传播规则:事件是否被传播取决于用户定义的传播规则。
对于LCR事件,可以指定下列级别的传播规则:
表级:传播指定表的DML或DDL。
用户级:传播指定用户下所有DML或DDL。
数据库级:传播源队列中所有的DML或DDL。
对于非LCR事件,用户可以创建自定义的规则来控制传播。
确保事件传输:
当在目的队列入队操作被提交时,用户入队事件传播成功。
捕获事件的成功必须完成下面两个动作:
事件被目的队列相关的应用进程处理;
事件成功的从目的队列传播到所有和当前队列相关的目的队列。
当事件成果的传播到目的队列,目的队列会确认事件的成功传播。如果一个源队列传播事件到多个目的队列,那么事件会一直保留在源站点队列中直到所有的目的站点都发送传播确认消息。
这种确认保证事件总是可以从源站点传播到目的站点,但是在某些配置情况下,源队列可能增长得超过优化值的设置。当源队列增长时,会使用更多的SGA内存并使用更多的磁盘空间。下面两个是源队列增长的常见原因:
如果事件无法传播到某个目的队列,则事件会一直存在于源队列中。这种情况会使得源队列不断变大,因此,需要经常的检测队列的状态来尽早的解决问题。
假设源队列传播事件到多个目的队列,且一个或多个目的队列的成功传播的确认比其他队列要慢很多。如果是这种情况,可以考虑在源站点上创建多个捕获进程,为比较慢的站点使用一个源队列,其他较快的站点使用另外的源队列。
Directed netwrok:Directed Network是指事件传播到目的数据库前经过一个或多个中间数据库。中间数据库的应用进程可以处理事件也可以不处理事件。使用directed network的好处是,源数据库和目的数据库之间可以不需要直接相连。源数据库和目的数据库之间即使没有直接的网络相连,但只有他们可以通过一个或多个中间数据库连接在一起,就可以配置流环境。如果配置了directed network,中间数据库一段事件内无法启动,或着从directed work中删除,则需要重新配置流环境。
队列推进和应用推进:利用中间数据库在directed network进行事件的传播存在两种方式:队列推进和应用推进。
队列推进:中间数据库发送出去的事件就是它所接收的事件。事件的源数据库就是事件发起的地方。
应用推进:中间数据库的应用进程首先应用接收到的事件,然后这些事件被捕获进程再次捕获,并发送出去。如果使用应用推进,则事件的源数据库变为当前的中间数据库。
队列推进和应用推进具有以下的不同:
对于队列推进,如果不存在捕获或传播转换的话,事件在传播过程中是不变的;对于应用推进,事件在中间数据库被应用,并重新捕获,因此,事件可能被冲突解决方案、应用处理过程(apply handle)和应用转换过程所修改。
对于队列推进,在目的数据库必须对每个源数据库的建立单独的应用进程;对于应用推进,由于事件在中间数据库应用并再次捕获,导致当事件传播到目的数据库时,源数据库数量减少,因此,需要的应用进程数量减少。
对于队列推进,源数据库和目的数据库之间存在着一个或多个中间数据库;对于应用推进,由于数据的再次捕获,对于目的数据库来说,源数据库就是与它直接相连的中间数据库。
在流环境中,可以包含队列推进和应用推进两种方式的组合。
队列推进的优点:
由于捕获只发生一次,因此性能比较高;
事件从源数据库传播到目的数据库所需时间较少,事件不需要在一个或多个中间数据库上应用并再次捕获;
通过调用事件中LCR包含的GET_SOURCE_DATABASE_NAME成员函数,可以很容易的确定源数据库;如果是应用推进,则必须使用流标识(Streams Tags)和应用处理过程(apply handle);
平行应用具有更好的伸缩性,由于具有更少的相关性,当多个应用进程处理时,具有更好的吞吐量;
如果一个中间数据库崩溃,可以重新设置队列的路由,并在捕获站点重设开始SCN,来重新配置端到端的捕获、传播和应用。如果使用应用推进,则需要更多的工作来配置端到端的捕获、传播和应用,这是由于目的数据库使用的SCN信息来自中间数据库,而确实这个SCN信息,则目的数据库无法进行修改的应用。
应用推进的优点:
流环境的配置更加简单,由于每个应用的事件都只来自与它直接相连的数据库,而不是来自多个远端源数据库;
在一个大型的流环境中,如果中间数据库应用修改,则需要数量较少的应用进程,从而使得整个环境更加容易检测和管理;
在一个存在多个源数据库的流环境中,用户可以将数据库添加到流环境中而不需要在每个数据库上都停止DML,并且等待所有被捕获的LCR都被传播、应用完成。
SYS.AnyData队列和用户消息:
流将消息装载到SYS.AnyData类型的队列中。这个队列叫流队列。流队列可以部署SYS.AnyData类型的用户事件。SYS.AnyData可以兼容各种数据类型。
利用SYS.AnyData包含消息负荷,发布应用程序可以将任意类型的消息入队到一个队列中,订阅应用程序可以通过出队API明确的出队消息,或通过应用进程隐含将消息出队列。如果订阅应用程序在远端数据库,可以将消息传播到远端站点,远端数据库中的订阅应用程序从本地队列中将消息出队列。远端订阅程序也可以通过各种标准协议,如:PL/SQL、OCI,直接从源队列将消息出队。
流内部操作高级队列,因此支持消息队列属性系统的所有标准特性。
SYS.AnyData包装用户消息负荷:
利用SYS.AnyData类型的静态成员函数Convert“data_type”(这里的datatype是需要转换的数据类型的名称),几乎可以将任意类型的消息负荷包装到SYS.AnyData类型中。
下列类型无法包装到SYS.AnyData类型中:Nested table、NCLOB、ROWID和UROWID。
下列类型可以直接包装到SYS.AnyData类型中,但这些类型不能引入到用户定义类型负载中:CLOB、BLOB、BFILE和VARRAY。
将用户消息入队、出队的程序环境:
使用PL/SQL入队用户消息:
使用PL/SQL将包含LCR的用户事件入队到流队列中,首先要创建入队的LCR。用户可以利用SYS.LCR$_ROW_RECORD类型的构造器,创建行记录LCR;利用SYS.LCR$_DDL_RECORD类型的构造器创建DDL LCR。然后可以利用SYS.AnyData.ConvertObject函数将LCR转换为SYS.AnyData负载,并利用DBMS_AQ.ENQUENCE过程入队。
使用PL/SQL将不包含LCR对象的用户信息入队到流队列中,用户可以使用一个SYS.AnyData.Convert*函数将用户信息转换为SYS.AnyData负载,并利用DBMS_AQ.ENQUENCE过程入队。
使用JMS或OCI入队用户消息:
使用JMS或OCI将包含LCR的用户事件入队到流队列中,必须将LCR表现为XML格式。为了构建一个LCR,使用oracle.xdb.XMLTpye类。LCRs定义在SYS方案中。使用$ORACLE_HOME/rdbms/admin目录下的catxlcr.sql脚本将LCR方案装载到SYS方案中。
使用OCI入队消息,执行的操作与将一个消息入队到指定类型的队列的操作是相同的。使用JMS入队消息,用户必须拥有DBMS_AQ、DBMS_AQIN和DBMS_AQJMS包的执行权限。
非LCR用户消息可以是用户定义类型或JMS类型的消息。
JMS类型包括:javax.jms.TextMessage、javax.jms.MapMessage、javax.jms.StreamMessage、javax.jms.ObjectMessage和javax.jms.BytesMessage。
如果使用用户定义类型,用户必须使用Jpublisher为消息产生java类,这些java类实现ORAData接口。
将事件加入队到流队列中,可以使用QueueSender.send方法或TopicPublisher.publish方法。
使用PL/SQL出队用户事件:
无论用户事件包含LCR还是其它类型的对象,都可以使用DBMS_AQ.DEQUEUE过程并指明SYS.AnyData作为负载。
使用JMS或OCI出队用户事件:
在流队列中,包含XML格式的LCR的用户事件被表现为oracle.xdb.XMLType。
非LCR用户事件可以是JMS类型或用户定义类型。
使用JMS从流队列中出队列消息,可以使用QueueReceiver、TopicSubscriber或者TopicReceiver方法。
使用OCI出队列事件,执行的操作与指定类型的出队的操作是相同的。
消息传播与SYS.AnyData队列
不同类型队列间传播事件的转化:
源队列和目的队列都是SYS.AnyData类型:不需要转换;
源队列指定类型,目的队列是SYS.AnyData类型:隐含转换;
源队列是SYS.AnyData类型,目的队列是指定类型:需要建立规则过滤消息,并建立用户自定义的转换。
用户定义类型消息
如果计划在流环境中入队、传播和出队用户定义类型的事件,则事件中包含的每种类型在流环境部署的每个数据库中都必须存在。
另外,每种类型都必须满足下列要求:
在每个数据库中类型的名称必须一致;
在每个数据库中类型必须处于相同的方案中;
在每个数据库中类型必须具有相同的形态;
在每个数据库中,类型都不能继承或演变;
类型中不能包括VARRAYS、NESTED TABLES、LOBS、ROWID和UROWID。
流队列和ORACLE RAC
在RAC环境中,拥有队列的实例在具有缓冲区。不同的实例可以拥有不同队列的缓冲。队列缓冲是SGA中,与流队列关联用于包含捕获队列的内存区域。
在RAC环境中,仅仅包含用户定义事件的流队列的行为和包含指定类型队列的行为是一样的。但是,如果流队列中包含捕获事件,则RAC环境必须满足下列条件:
包含捕获事件的流队列的每个队列表,都必须通过DBMS_STREAMS_ADM包的SET_UP_QUEUE过程创建;
使用的流队列来处理捕获事件的所有捕获进程和应用进程都必须由流队列的拥有实例来启动;
当把捕获进程传播到RAC环境的目的数据库中时,必须通过指定实例的数据库链传播到目的队列的拥有实例;
AQ时间管理器必须在所有实例上运行。
如果包含目的队列的队列表的拥有实例变得不可用,则队列的拥有权自动转换到RAC的其他实例上。如果发生这种情况,则需要在远端数据库手工重新配置数据库链到新的目的队列的拥有实例。通过查询数据字典DBA_QUEUE_TABLES可以找到队列表的拥有实例。
流的部署和传播的架构
总体来说,流队列和传播使用的底层基础是高级队列。但是,和高级队列将所有事件部署到队列表中不一样的是,流队列拥有共享内存中的队列缓冲区来部署捕获事件。
队列缓冲区:队列缓冲区是与流队列管理用来包含捕获事件的SGA内存。用户定义LCR事件和用户定义消息则只保存在队列表中,而不保存在队列缓冲区中。
队列缓冲区可以提高性能,但是队列缓冲区中的内容在数据库正常或非正常关闭时丢失。如果全库恢复完毕,则流机制会自动恢复队列缓冲区中的内容。
传播job:流的传播在内部配置时使用DBMS_JOB包。
如果数据库以RESTRICTED SESSION方式启动,则传播JOB不会启动。如果是通过ALTER SYSTEM语句将数据库处于RESTRICTED SESSION模式,则当前的JOB继续运行,直到完成,而新的传播JOB不会启动,直到结束RESTRICTED SESSION状态。
安全队列:安全队列是指高级队列的代理明确指出一个或多个数据库用户可以对队列进行操作。安全队列的拥有者可以执行所有的队列操作,而其他用户除非被配置为这个安全队列的安全队列用户,否则不能对这个安全队列执行操作。
所有使用DBMS_STREAMS_ADM包的SET_UP_QUEUE过程创建的队列都是安全队列。其中queue_user参数输入的用户被配置为这个队列的安全队列用户。队列用户被授予对这个队列的ENQUEUE和DEQUEUE权限。如果要将事件入队或将事件从队列中出队,队列用户必须拥有DBMS_AQ包的执行权限,而SET_UP_QUEUE过程并不会授予这个权限。
事务队列和非事务队列:事务队列是指用户入队的事件可以合并为一组,并作为一个事务被应用。也就是说,应用进程在执行了一组用户入队事件后会执行一个COMMIT操作。DBMS_STREAMS_ADM包的SET_UP_QUEUE过程创建的队列总是事务队列。
非事务队列是指每个用户入队事件都是一个事务。应用进程在执行每个用户入队事件后都会执行一个COMMIT操作。
事务队列和非事务队列的区别只体现在用户入队事件上。对于捕获事件,应用进程总会保留源数据库中的事务性。
传播使用的流数据字典:当捕获进程被创建时,数据字典的一个副本叫做流数据字典也自动产生。流数据字典是源数据库上主数据字典中一些信息的多版本拷贝。流数据字典在权衡规则和创建LCR时将对象编号,对象版本信息和内部列的编号映射为表名、列名和列的数据类型。由于捕获事件只保存数字而不是名称,因此,这种映射关系使得捕获事件尽可能的小。
源数据库中流数据字典的这种映射信息可能在传播捕获事件权衡规则时会使用。为了保证映射规则在传播中可用,Oracle在流传播的每个站点上自动传入多版本的流数据字典。Oracle自动从源数据库的流数据字典把相关信息通过内部消息的方式传播到所有从源数据库接收捕获事件的数据库中。











