stream replication初步研究
10gR1中Stream的单源表级复制配置
Oracle stream在9iR2中被发布,在10gR1中一些新技术被揉合进去,虽然它的功能被增强了很多,但它的稳定性还有待提高;本文描述stream在9iR2中的配置步骤,同时也兼顾10gR1的新功能,很明显,10gR1中stream配置简化了很多。
Stream的单源复制分为“一对一”与“一对多”两种,虽然可以在一个数据库模式间配置复制,但实用价值并不大,故先为大家介绍在两个数据库之间的“一对一”复制,“一对一”复制配置成功后,很容易扩展成“一对多”或多源复制。
不管是单源复制还是多源复制,它们都有三种复制模式:
表级复制 可以选择单独复制某些表
模式复制 可以选择对某个模式复制
库级复制 除sys/system模式的复制
这三种复制模式中,步骤其实是一样的,都需要经历以下步骤,本文先以单表复制为例,给大家介绍配置方法,然后添加更多的表进行复制,介绍表级复制后,接着介绍模式复制或库级复制,这两种都比表级复制简单;从“一对一”复制扩展到“一对多”复制或多源复制,无非是重复“一对一”复制配置过程。
配置数据库在归档模式(源库必做)
在10gR1中,自动归档参数log_archive_start已废弃,可用archive log list在sqlplus下查看数据库是否处于归档模式;若需激活数据库的自动归档,数据库要启动到mount状态,在sqlplus下执行alter database archivelog即可,源数据库必须在归档模式。
SQL>startup mount;
![]()
SQL>alter database archivelog;
![]()
SQL>alter database open;
配置初始化参数(源库、目标库必做)
在9iR2中,需要配置以下参数让实例支持stream replication,其中job_queue_prcesses与aq_tm_processes参数可以适当地大一些,避免触发异常;若aq_tm_processes参数为0,则会导致入队的LCRs不被写入buffered queue而被直接写入queue 表(写入disk),这样从捕获到传播等环节的性能都大受影响;global_names参数必须设置为ture,compatible参数必须设置为9.2.0以上版本号,源库与目标库配置成如下所示:
SQL>alter system set compatible='9.2.0' scope=spfile;
![]()
SQL>alter system set aq_tm_processes=4 scope=spfile;
![]()
SQL>alter system set global_names=true scope=spfile;
![]()
SQL>alter system set job_queue_processes=8 scope=spfile;
![]()
SQL>alter system set log_parallelism=1 scope=spfile;
![]()
SQL>alter system set logmnr_max_persistent_sessions=1 scope=spfile;
在9iR2中,并没有为stream提供配置stream pool的参数,stream pool从shared pool中分配,约占用shared pool的10%;在10gR1及后续版本中,log_ parallelism参数已废弃,设置stream_pool_size参数为stream pool分配内存,最好大于200m,如果归档日志文件较大或日志切换较频繁,需要适量增加stream pool的内存来存储队列。
SQL>alter system set stream_pool_size=200m scope=spfile;
参数logmnr_max_persistent_sessions控制stream源库中捕获进程的数量,每个logmnr会话产生三个相关进程:reader process、builder process、preparer process;每个logmnr会话需要10m内存,如果stream pool设置太小或没有设置,可能会触发ora-04031错误,若logmnr_max_persistent_sessions参数增加,则需要调整stream pool或shared pool。下面这段信息摘自altert_sid.log文件:
LOGMINER: Parameters summary for session# = 1
![]()
LOGMINER: Number of processes = 3, Transaction Chunk Size = 1
![]()
LOGMINER: Memory Size = 10M, Checkpoint interval = 10M
![]()
LOGMINER: StopMiningThreshold = 1M
![]()
LOGMINER: session# = 1, reader process P000 started with pid=16 OS id=1090
![]()
LOGMINER: session# = 1, builder process P001 started with pid=17 OS id=1092
![]()
LOGMINER: session# = 1, preparer process P002 started with pid=18 OS id=1094
在设置参数时,open_links应该比目标站点数多,由于该参数是不允许动态修改,所以要预留的多一些;archive_lag_target参数设置时也要估计日志的切换频率以及业务最大可接受的数据延迟,设置不当将会导致日志切换频繁,造成性能问题。
配置复制管理员并授权(源库、目标库必做)
需要在源库与目标库上创建一个stream 复制管理员,同时也要避免使用system表空间,建议创建一个单独的表空间来容纳队列表;对于stream复制管理员,某些package需要单独授权,比如用dbms_rule_adm包来授权创建rule、rule set等权限。
create user strmadmin identified by strmadminpw
![]()
default tablespace &tablespace_name quota unlimited on &tablespace;
![]()
grant connect, resource, select_catalog_role to strmadmin;
![]()
grant execute on dbms_aqadm to strmadmin;
![]()
grant execute on dbms_capture_adm to strmadmin;
![]()
grant execute on dbms_propagation_adm to strmadmin;
![]()
grant execute on dbms_streams_admto strmadmin;
![]()
grant execute on dbms_apply_adm to strmadmin;
![]()
grant execute on dbms_flashback to strmadmin;
![]()
connect / as sysdba;
![]()
begin
![]()
dbms_rule_adm.grant_system_privilege(
![]()
privilege => dbms_rule_adm.create_rule_set_obj,
![]()
grantee => 'strmadmin',
![]()
grant_option => false);
![]()
end;
![]()
/
![]()
begin
![]()
dbms_rule_adm.grant_system_privilege(
![]()
privilege => dbms_rule_adm.create_rule_obj,
![]()
grantee => 'strmadmin',
![]()
grant_option => false);
![]()
end;
![]()
/
![]()
在10gR1中,stream 复制的授权方式得到了改进,dbms_streams_auth可以完成一站式的授权,避免了由授权失误而导致的复制失败,dbms_stream_adm包的grant_admin_privilege过程统一处理授权工作,revoke_admin_privilege过程统一处理收回授权工作,将dba权限授予stream管理员是必须的。
create user strmadmin identified by strmadminpw
default tablespace tbs_test quota unlimited on tbs_test;
grant connect, resource, dba to strmadmin;
connect / as sysdba;
begin
dbms_streams_auth.grant_admin_privilege(
grantee => 'strmadmin',
grant_privileges => true);
end;
/
初始化队列及队列表(源库、目标库必做)
dbms_streams_adm的set_up_queue过程可以为stream创建复制需要的队列及队列表,它的remove_queue过程可以删除存在的队列及队列表;在用set_up_queue过程创建队列及队列表时,如果没有为队列指定名称,将会按默认的名称创建队列及队列表。
connect strmadmin/strmadminpw;![]()
exec dbms_streams_adm.set_up_queue();
配置tns并创建db link(源库、目标库必做)
在创建db link时,要检查open_links(>=4)与global_names(true)初始化参数的值,同时要确保配置好网络服务名,用tnsping测试后,还要在sqlplus下验证是否可以通过网络服务名进行远程登陆。
connect strmadmin/strmadminpw@&tns_name;
![]()
create database link lnk_&lnk_name
![]()
connect to &user_name identified by &password using ‘&service_name’;
创建测试模式(源库可选)
在进行stream测试时,如果源库及目标库没有现成的模式(user)用于stream复制,需要创建一个新模式及测试用的表供stream使用;在创建用于复制的表时,只在源库中创建即可,目标库可以通过exp/imp来初始化表结构及初始数据等;在10gR1及后续版本中,可以用expdp/impdp、rman等来同步目标库中的表,表创建后要授权给stream管理员。
connect / as sysdba;
![]()
create user scott identified by &password
![]()
default tablespace &tablesapce_name quota unlimited on &tablespace_name;
![]()
grant connect,resource to scott;
![]()
connect scott/&password;
![]()
create table dept(id int constraint pk_dept_id primary key,name varchar2(20));
![]()
alter table dept add supplemental log group log_group_dept_pk (id) always;
![]()
grant all on dept to strmadmin;
![]()
创建复制用的表表需要注意一些细节问题:
表必须有主键或唯一索引
表存在组合主键或组合唯一索引时要为表添加附加日志(supplemental log)
表的约束要指定名称(主键、唯一约束、默认值、check约束)
主键与唯一索引保证目标库中数据应用的准确性,如果表中有组合索引,就需要为表或数据库添加附加日志,添加附加日志虽然会增加日志切换的频率,但可以避免由于更新组合主键中的某一字段而导致的更新扩大;表中的约束一定要指定名称,因为自动命名的约束在源库与目标库是不同的,当源库删除约束时,目标库中可能找不到该约束。
配置数据库的捕获进程(源库必做)
捕获进程依据复制源库定义的捕获规则,将符合捕获规则的事务及其它操作等(含ddl)等,从redo logs(含online redo log与archive redo log)中挖掘,并把捕获的change vector包装成LCRs(logical change records)写入源站点的消息队列中。
规则是stream预定义的一组LCRs过滤机制,在捕获、传播、应用进程中都会用到,常用的规则有DDL rules与DML rules等,比如在配置捕获进程时,指定DML rules参数为true、DDL rules参数为false,则表示捕获进程只捕获DML(insert、update、delete、merge等)改变而不捕获DDL(alter table、truncate table等)改变。
sub-set rules是DML rules的延伸,允许stream复制的进程对捕获、传播、应用等过程进行更精细的控制,比如可以创建只捕获、传播、应用符合某种条件的LCRs(比如delete from test where status=1)的sub-set rules。
捕获规则分为表级捕获规则、模式级捕获规则、数据库级捕获规则等。为了简化stream的配置,dbms_streams_adm的add_table/schema/global_rules过程在配置各级捕获进程时,会根据指定的参数创建相应的捕获规则。如果想自己定义或创建各种级别的规则,可以用dbms_rule_adm的create_rule_set、create_rule、add_rule过程完成创建规则集、创建规则、并添加规则到规则集的过程。

图5 捕获进程
在下面源库配置捕获示例中,必须注意要用stream管理员身份登陆,table_name参数输入的是schema.table_name格式,注意stream管理员必须有对schema.table_name表执行dml/ddl的权限;streams_type参数要指定是“capture”,streams_name参数可自已命名,queue_name参数是在初始化队列时dbms_streams_adm的set_up_queue过程指定的队列名,如果在初始化队列时,没有为set_up_queue过程指定队列名,那stream就用默认的队列名streams_queue,它的输入格式是strmadmin.streams_queue,include_dml与include_ddl指示dbms_rule_adm为捕获进程配置rule-sets与rules;本示例的作用是指:capture进程在挖掘日志时,需要捕获scott.dept表所发生的ddl及dml等操作。
connect strmadmin/strmadminpw@&source_tns;
![]()
begin
![]()
dbms_streams_adm.add_table_rules(
![]()
table_name => 'scott.dept',
![]()
streams_type=> 'capture',
![]()
streams_name => '&capture_name',
![]()
queue_name=> 'strmadmin.streams_queue',
![]()
include_dml => true,
![]()
include_dd=> true);
![]()
end;
![]()
/
配置捕获进程有两种方式:一种是用dbms_streams_adm的add_table_rules过程,另一种是用dbms_capture_adm的create_capture过程;在用dbms_capture_adm配置捕获进程时,必须用dbms_capture_adm的prepare_table_instantiation过程来配置需要进行捕获的表,同时还必须用dbms_rule_adm来创建捕获规则及规则集,显然用dbms_capture_adm没有dbms_streams_adm方便。
配置数据库的传播进程(源库必做)
在stream中,events用sys.anydata类型的队列进行传播,有两种格式的events可以被传播:LCRs与User-Messages;LCRs是捕获进程在redo log中挖掘出来的dml或ddl被打包成sys.anydata格式,User-Messages是用户或应用入队的sys.anydata。

图6 传播过程
传播进程是连接源库队列与目标库队列的桥梁,其实传播进程是一个job queue进程,所以在设置初始化参数时,要求aq_tm_processes与job_queue_processes参数大于目标站点数,避免“一对多”或“多对多”复制时产生调度拥挤。
connect strmadmin/strmadminpw@&source_tns;
![]()
begin
![]()
dbms_streams_adm.add_table_propagation_rules(
![]()
table_name => 'scott.dept',
![]()
streams_name => '&propagatin_name',
![]()
source_queue_name => 'strmadmin.streams_queue',
![]()
destination_queue_name=> 'strmadmin.streams_queue@&target_lnk',
![]()
include_dml => true,
![]()
include_ddl => true,
![]()
source_database => '&source_lnk');
![]()
end;
![]()
/
在上例的add_table_propagation_rules过程中,table_name参数是需要传播到目标库的表,格式是schema.table_name,streams_name参数可随意,即传播名称,source_queue_name参数是源队列名,默认是stremadmin.streams_queue,destination_queue_name参数中的@target_db_lnk是数据库链接,即指向目标库的strmadmin.streams_queue,include_dml与include_ddl参数表示可以传播scott.dept表的dml/ddl操作到目标库,source_datbase参数表示指向源库的数据库链接。
传播进程的传播类型有三种:表级、模式、全库。分别由dbms_streams_adm package的add_table/schema/global_propagation_rules过程来完成,传播规则可以与捕获规则不同;传播进程也可用dbms_propagation_adm的create_propagation过程创建,则传播规则需手工配置,而它的job queue进程必须用dbms_aqadm 进行配置。

图 7传播进程
如图7所示,传播进程由job queue进程来代理,但它与job进程有一些细微的区别,比如传播进程的interval属性为null时,表示传播进程在当前的任务完成后(默认单次任务时间是4分10秒),潜伏3秒,再进入下次任务处理中,如果当前队列为空,则一直处于潜伏状态,直到被捕获进程唤醒,如果想改变传播进程的调度信息,可以使用dbms_aqadm package的alter_propagation_schedule过程。
当启动配置有stream的数据库时,需要先启动目标库,然后再启动源库,否则在源库启动时,在alert_sid.log中报ora-2068/1033号错误,有可能导致stream的捕获进程的状态置为"paused for flow control",但不需要太担心,重启源库捕获进程就正常啦,在动态性能视图v$streams_capture的state记录了捕获进程的状态,也可查dba_capture字典表。
目标库初始数据同步(目标库必做)
当源库配置捕获、传播进程后,需要将源库待复制的表与目标库进行同步,在9iR2中只能使用exp/imp来同步初始数据,在10gR1及后续版本中,还可以用expdp/impdp、rman等进行源库与目标库的初始数据同步(设置目标库中对象的scn与源库相同)。
exp/imp、expdp/impdp适用于表级、模式级、库级初始数据同步,rman适用于数据库规模较大的初始数据同步;expdb/impdp与exp/imp相比,同步初始数据的速度更快,rman在全库复制方面大大降低初始数据同步的时间。
在用exp/imp时,源库导出表、模式或库,在目标库导入表、模式或库。如果源库中的表创建了附加日志(supplemental log),在目标库需要手工删除导入的附加日志,通过数据字典视图user_log_groups可知那些表存在附加日志。用exp/imp表级(模式/库级)导出(9iR2/10gR1通用)示例:
exp scott/tiger@&source_tns FILE=exp.dmp TABLES=dept OBJECT_CONSISTENT=y
![]()
imp scott/tiger@&target_tns FILE=exp.dmp FULL=y STREAMS_INSTANTIATION=y
![]()
connect scott/tiger@&target_tns;
![]()
alter table scott.dept drop supplemental log group log_group_dept_pk;
设置初始scn(目标库必做)
用exp/imp、expdm/impdp、rman进行数据同步,同时也可以对初始scn进行设置,比如exp时OBJECT_CONSISTENT=y参数与imp时STREAMS_INSTANTIATION=y参数,它们就已经完成了初始scn的设置。
在不需要初始数据同步的情况下,可以跳过用exp/imp初始表结构与数据,可以直接在源库与目标库创建表,用dbms_apply_adm的set_table/schema/global_instantiation_scn过程直接设置目标库中表的初始scn。如下例所示,用stream管理员连接到源库,用dbms_flashback的get_system_change_number过程求得源库的scn,然后调用set_table_instantiation_scn过程调置目标库的表的初始scn。
connect strmadmin/strmadminpw@&source_tns;
![]()
declare
![]()
v_scn number;
![]()
begin
![]()
v_scn := dbms_flashback.get_system_change_number();
![]()
dbms_apply_adm.set_table_instantiation_scn@&target_lnk(
![]()
source_object_name => 'scott.dept',
![]()
source_database_name => '&source_lnk',
![]()
instantiation_scn => v_scn);
![]()
end;
![]()
/
![]()
过程set_table_instantiation_scn的参数source_database_name代表链接到源库的数据库链接,@target_lnk代表链接到目标库的数据库链接,它调用的是目标库上dbms_apply_adm的set_table_instantiation_scn过程;参数source_object_name指需要从源库取得该对象的scn,并将获得的scn同步到目标库中的对象;参数instantiation_scn是从源库求得的scn值;设置了表、模式、库的初始scn后,可以从dba_apply_instantiated_objects字典表中确认;感觉这个过程的实现比较别扭…理解起来比较难。
配置并启动目标库的应用进程(目标库必做)
当源库的捕获、传播进程配置完毕,目标库的初始数据同步及初始scn设置后,就可以配置并启动目标库的应用进程;可以用dbms_streams_adm的add_table/schema/global_rules过程来配置应用进程,也可以有dbms_apply_adm的create_apply及alter_apply等过程来创建并配置应用进程,以下是用dbms_streams_adm配置应用进程的示例:
connect strmadmin/strmadminpw@&target_tns;
![]()
begin
![]()
dbms_streams_adm.add_table_rules(
![]()
table_name => 'scott.dept',
![]()
streams_type => 'apply',
![]()
streams_name => '&apply_name',
![]()
queue_name=> 'strmadmin.streams_queue',
![]()
include_dml=> true,
![]()
include_ddl => true,
![]()
source_database => '&source_lnk');
![]()
dbms_apply_adm.start_apply(apply_name => '&apply_name');
![]()
end;
![]()
/
![]()
在用dbms_streams_adm的add_table_rules过程配置应用进程时,它的参数streams_type必须是apply,参数streams_name可随意,参数source_database代表链接到源库的数据库链接,include_dml/ddl指示应用进程可以应用捕获到的dml及ddl操作。
配置完应用进程后,需要用dbms_apply_adm的start_apply过程来启动应用进程,如果想修改应用进程的参数,需要先停下应用进程,然后修改后再启动应用进程,如果不停下应用进程修改应用参数将会报错,修改应用参数示例:
connect strmadmin/strmadminpw@&target_tns;
![]()
begin
![]()
dbms_apply_adm.stop_apply(
![]()
apply_name => '&apply_name');
![]()
dbms_apply_adm.set_parameter(
![]()
apply_name => '&apply_name',
![]()
parameter => 'disable_on_error',
![]()
value => 'n');
![]()
dbms_apply_adm.start_apply(
![]()
apply_name => '&apply_name');
![]()
end;
![]()
/
![]()
如示例所示,用dbms_apply_adm的stop_apply过程停下应用进程,用set_parameter过程修改应用进程的参数,用start_apply过程来启动应用进程,可以通过all_apply_parameters字典表查看应用进程的当前的参数设置。
启动源库的捕获进程(源库必做)
其实把启动源库的捕获进程安排到最后不是必须的,但这样做的好处是避免源库的队列因没有传播而过度臌胀,启动源库的捕获进程用dbms_capture_adm的start_capture过程,停止源库的捕获进程用stop_capture过程。
connect strmadmin/strmadminpw@&source_tns;
![]()
begin
![]()
dbms_capture_adm.start_capture(
![]()
capture_name => '&capture_name');
![]()
end;
![]()
/
![]()
stream复制环境的监控(源库与目标库必做)
如果运气好的话,单源复制已经成功运作啦,实际上在手工配置过程中,稍微的一些失误都可能导致复制的失败,主要是stream在配置过程中,有些过程对输入的实参没有验证有效性,比如add_table_rules过程的source_database_name参数随便输入都不会报错,实际上该实参所代表的database link如果不存在,复制将不会成功。
当我们配置stream成功后,还要面对相当多的未知错误,如何监控stream环境?确保能及时发现并解决复制故障;主要从捕获、传播、应用三个方面进行监控:捕获与传播进程集中在源库,应用进程在目标库,所以对它们的监控脚本一般会分成源库与目标库两部分,然后通过linux下的cron守护进程进行调度。
源库需要监控那些资源?
? 初始化参数 某些初始化参数修改会导致复制环境失败
? 数据库链接 确保数据库链接可以正常工作
? 捕获进程状态 确保复制期间捕获进程的状态一直是enabled
? 传播进程状态 确保传播job存在及它的broken状态是N
? 后台警告日志 收集警告日志并识别它是否是stream引起的
? 队列是否溢出 队列溢出将严重影响复制的性能
目标库需要监控那些资源?
? 初始化参数 某些初始化参数修改会导致复制环境失败
? 数据库链接 确保数据库链接可以正常工作
? 应用进程状态 确保复制期间应用进程的状态一直是enabled
? 后台警告日志 收集警告日志并识别它是否是stream引起的
? 应用错误字典 查询dba_apply_error字典确定应用进程是否遇到麻烦
? 数据延迟时间 确保数据延迟时间在业务系统接受的范围内
捕获进程与应用进程在实例启动时将恢复到实例关闭前的状态,如果实例关闭前它们的状态是enabled,实例启动后它们的状态依旧会是enabled状态;传播进程是job进程,如果job进程试探16次失败后,传播job将会被自己删除,所以传播进程不仅要监控它的broken状态是否是N,还要监控它们是否存在,如果传播job不存在,需要用dbms_aqadm的schedule_propagation过程重新配置传播job。
在9iR2及10gR1版本,oracle在metalink分别为它们提供了监控stream的脚本,该脚本涵盖了stream复制的各个方面,比我上面所示的更为详细,如果有metalink帐号的话,你可以通过查看Note:273674.1,查看9i及10g的stream监控脚本。
在10gR1中,metalink也为stream复制提供了基于命令行的监控工具,它支持9iR2及10gR1,可以通过Note:290605.1找到strmmon工具,按照该页面的描述,下载、编译并运行strmmon工具,strmmon可以监控stream复制中源库与目标库中的LOG、CPxx、MEM%、Qx、PRxx、APxx等方面,可按如下方法编绎:
tar -xf strmmon.tar
make -f strmmon.mk strmmon