OGG复制进程延迟高,优化方法一(使用索引)

日常运维过程中,可能发现OGG同步进程延迟很高;

本篇介绍其中的一种方式。

OGG复制进程,或者说同步进程及通过解析ogg trail文件,输出dml语句,在目标库执行dml操作,那么延迟高可能性其一、执行dml操作效率太低。 本篇不考虑并发过高或其它原因。 本次只考虑是执行update or delete的时候SQL效率执行太差!

导致OGG复制进程延迟很高。

 

GGSCI > info all

Program     Status      Group       Lag at Chkpt  Time Since Chkpt

MANAGER     RUNNING                                                                                    

REPLICAT    RUNNING     RP10        00:00:00      00:00:03  
延迟说明参考
//www.onekbit.com/ViewBlog/blog/BID20200408100342

一 、Time Since Checkpoint 过高

       指ogg的extract或replicat进程产生最近的一个检查点,再从这个检查点到目前为止有多长时间没有更新了,即最近一个检查点与当前系统时间的时间差。该值可以通过info看到是在不断变化(特别是当处理长会话时,会持续增长,直到处理完该长会话)。

       对于复制进程来说,如果Time Since Chkpt 延迟有2个小时,说明这个进程存在2个小时检查点未更新,也就说明有一个或多个组合成的大事务,执行了2个小时,并未执行成功???

      正常情况下,OGG遇到异常报错,导致OGG进程中断Time Since Chkpt 50个小时后,解决报错后,启动该进程,一般来说2分内,会执行成功最少一个事务,会写入新的检查点,延迟的50个小时,会自动转换为lag at chkpt 50小时延迟。

      异常情况或者说需要优化调整的情况是,启动进程,发现time since chkpt 延迟一直递增,不减少。说明存在事务未执行完毕。  

   实际遇到的情况1,进程同步4个表,其中一个表很大10G,并且目标端无主键!!! 

    因此目标端执行一条update sql执行效率非常低。  如何处理???

     

1.表存在主键
select   *   from   user_cons_columns   
  where   constraint_name   =   (select   constraint_name   from   user_constraints   
              where   table_name   =   'BST_FAVORITE'  and   constraint_type   ='P');   
2.对OGG复制进程添加参数指定主键列
map source_owner.table_name target target_owner.table_name;
添加参数
map source_owner.table_name target target_owner.table_name,keycols(primary_column_name);
3.对于不存在主键的表呢???
select count(*) from xxx; 得到表的数量,如果表很大,不执行最好。
通过dba_tab_columns 根据NUM_DISTINCT 得到最多distinct的列,及选择性好的列。
SQL> select COLUMN_NAME,NUM_NULLS,NUM_DISTINCT,to_char(LAST_ANALYZED,'yyyy-mm-dd') as "date" from dba_tab_columns where owner='cc' and table_name='cc' order by 3;
结合表的索引列
select a.uniqueness 索引类型,b.index_name 索引名称,b.column_name 字段 from user_indexes a ,user_ind_columns b
where a.table_name=b.table_name and a.index_name = b.index_name
and a.table_owner=upper('SAPSR3'and  a.table_name='ANLU' order by a.uniqueness desc;
  如果存在索引,并且选择性足够好,虽然不是主键列,但是可以直接使用keycols指定;
  如果不存在索引? 或者索引的选择性不够好,可以新增一个选择性好的索引,随后使用keycols进行指定。  
!!! 缺陷或者风险在于,因为是非主键,及时选择性好,如果存在null值,还是无法使用该索引,OGG 进程对应数据库session 执行SQL还是慢。 但是正常情况下可以解决该问题。
并且存在一个疑问,如果假设源端修改了一条记录,但是选择性好的column_name 在目标端对应2条记录,并且ogg复制进程使用keycols参数指定?  那么目标端是修改更新2条记录? 还是1条记录呢???
后续进行测试。

 

二、Lag at Chkpt

 lag是复制进程处理最后一条记录的操作系统时间和此条记录在trail文件中记录的时间戳的差值,这里需要注意的是lag延迟只有在检查点更新时才会更新,所以这个值不是实时更新的,具有一定的离散性,实际上应该理解成最后一个检查点的最后一条记录与当前系统时间的时间差。 

借鉴

 Replicat负责数据的入库,一般速度相对于主extract和data pump较慢,容易产生较大延迟。当replicat出现延迟后,需要对进程进行调优或者拆分,具体步骤参照本文档上一节。一般调优完成后,在日常业务状态下应当不存在较大延迟(一般几秒到一分钟以内);
当出现批处理时,可以允许一定的延迟,一般以不影响第二天的正常业务为准 – 例如,如果批处理每天早上4点前结束,可以控制延迟在2小时以内。 因此,首先需要确定OGG复制所允许的最大延迟在日常业务和批处理时的目标是什么,然后一旦达不到此目标就要依据上上面介绍的方法进行性能的调优。

自我理解:  按照我们实际运维的情况,OGG同步数据也是根据业务要求分级别的。

    例如OGG链路1,是用于数据报表生成,那么必须保证业务每天在8点~10点之间是OGG无延迟,否则延迟高哪怕是10分钟,也可能导致报表不准确,因此这种OGG链路的复制 不允许在7~10点存在明显延迟。

   例如OGG链路2,用于OGG灾备环境,重要性没那么重要,因此保证OGG复制进程1天内或者3天内数据同步即可。 

   站在运维的角度,需要结合实际情况考虑OGG重要程度,进行评估。  如果不影响业务,纯粹的延迟可以忽略。

   实际遇到延迟50小时。
 本次讲述通过索引加快OGG同步方式;

2.1 定位OGG复制进程在oracle数据库中的Session


$ps -ef|grep RP10
PID
20 RP10 ······
$ps -ef|grep 20
PID
60864 LOCAL=NO [OGG Session process]
SQL> select s.sid,s.serial#,sql_id,p.program from v$process p,v$session s where p.addr=s.paddr and p.spid=60864;

       SID    SERIAL# SQL_ID         PROGRAM
------------------------------------------------
      2276    37851 2j664   oracle@cc (TNS V1-V3)

2.2 定位造成OGG复制进程延迟过高的SQL

通过ash视图,查询1天内这个OGG进程 session ,都在执行什么sql ,event信息。 ash视图间隔1s采样 active session 1次。因此捕捉到的次数越多,说明消耗花费的时间越多。
select
sql_id,event,BLOCKING_SESSION,CURRENT_OBJ#,count(*) from v$active_session_history where SAMPLE_TIME>sysdate-1 and SESSION_ID=2276 and SESSION_SERIAL#=37851
group by sql_id,event,BLOCKING_SESSION,CURRENT_OBJ# order by 5,4; SQL_ID EVENT BLOCKING_SESSION CURRENT_OBJ# COUNT(*) ------------- ---------------------------------------------------------------- ---------------- ------------ ---------- 1n7zz8wb86jpw 0 16 088mh1tws6wtm -1 19 2jt8ttg6b42b4 0 75 9y2087cvvr4r9 0 115 1n7zz8wb86jpw -1 4575 2cc4 -1 18877 9y2087ccc9 -1 62497 10 rows selected. select * from table(dbms_xplan.display_cursor('2ccb4')); SQL> select * from table(dbms_xplan.display_cursor('9ycc')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID xx, child number 0 ------------------------------------- DELETE FROM "W"."DT" WHERE "U_ID" = :b0 AND 多个列 ······ AND "C_SORT" = :b18 AND ROWNUM = 1 PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- Plan hash value: 3610703515 ---------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------------------- | 0 | DELETE STATEMENT | | | | 24025 (100)| | | 1 | DELETE | DT | | | | | |* 2 | COUNT STOPKEY | | | | | | |* 3 | TABLE ACCESS FULL| DT | 1 | 3312 | 24025 (1)| 00:04:49 | ---------------------------------------------------------------------------------------------------- PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(ROWNUM=1) 3 - filter(("U_ID"=:B0 AND 多个列······ "O_SORT"=TO_NUMBER(:B18))) 35 rows selected.

距离,两个SQL 同一个表,都是delete操作,几乎相同。 执行几乎全表扫描,表10G,当然慢了,对吧? 执行删除1条记录,需要访问10G的数据。。。不慢才怪。

2.3 创建索引,加快OGG进程同步速度。

查询表相关索引,不存在。
select
index_owner,index_name,table_name,column_name,column_position from dba_ind_columns where table_name='DT' order by index_name,column_position; null

查询列的选择性
select * from dba_tab_col_statistics where table_name='DT' order by column_name; ``````` OWNER TABLE_NAME COLUMN_NAME NUM_DISTINCT LOW_VALUE ------------------------------ ------------------------------ ------------------------------ ------------ ---------------------------------------------------------------- HIGH_VALUE DENSITY NUM_NULLS NUM_BUCKETS LAST_ANAL SAMPLE_SIZE GLO USE AVG_COL_LEN HISTOGRAM ---------------------------------------------------------------- ---------- ---------- ----------- --------- ----------- --- --- ----------- --------------- W DT U_ID 0 0 0 0 10-JUL-20 YES NO 0 NONE ······ 19 rows selected. SQL> select count(*) from "W"."DT"; COUNT(*) ---------- 724643
GGSCI > stop Ogg_process
!注意,本次知道OGG对应是灾备,不存在大量相关业务,慎用no_invalidate>false exec dbms_stats.gather_table_stats(ownname
=>'W',tabname=>'DT',cascade=>true,degree=>8,estimate_percent=>10,no_invalidate=>false) ;
再次查询
OWNER TABLE_NAME COLUMN_NAME NUM_DISTINCT LOW_VALUE
------------------------------ ------------------------------ ------------------------------ ------------ ---------------------------------------------------------------- HIGH_VALUE DENSITY NUM_NULLS NUM_BUCKETS LAST_ANAL SAMPLE_SIZE GLO USE AVG_COL_LEN HISTOGRAM ---------------------------------------------------------------- ---------- ---------- ----------- --------- ----------- --- --- ----------- ---------------
W                           DT                     U_ID      59346 3030303039366339386466663436356439636636653430663366323138383365 

6666666561356132333564363430616462303462643861393832383734303361 .00001685 0 1 08-AUG-20 70833 YES NO 32 NONE 19
rows selected.
exec dbms_stats.gather_table_stats(ownname=>'W',tabname=>'DT',cascade=>true,degree=>8,estimate_percent=>10,no_invalidate=>false) ;

查询列的选择性
select OWNER,TABLE_NAME,COLUMN_NAME,NUM_DISTINCT,NUM_NULLS,LAST_ANALYZED from dba_tab_col_statistics where table_name='DT' order by NUM_DISTINCT; 
OWNER TABLE_NAME COLUMN_NAME NUM_DISTINCT NUM_NULLS LAST_ANAL

------------------------------ ------------------------------ ------------------------------ ------------ ----------
xx O_SORT 49853 0 08-AUG-20
xx U_ID 60063 0 08-AUG-20 19
rows selected.


select count(*) from ( select distinct U_ID,O_SORT from xx.xx);
COUNT(
*)
----------
692924
create index xx.xxon xx.cc(U_ID,O_SORT) parallel
6;
alter index xx.xx parallel
1;
exec dbms_stats.gather_table_stats(ownname=>'W',tabname=>'DT',cascade=>true,degree=>8,estimate_percent=>10,no_invalidate=>false) ;

GGSCI (obcdb36) 3> start ogg_process
SQL> select * from table(dbms_xplan.display_cursor('2jt8ttg6b42b4'));
----------------------------------------------------------------------------------

Plan hash value: 969284704
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | | | 2 (100)| |
| 1 | DELETE | cc| | | | |
|* 2 | COUNT STOPKEY | | | | | |
|* 3 | TABLE ACCESS BY INDEX ROWID| cc| 1 | 191 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IND_ID_SORT | 1 | | 2 (0)| 00:00:01

至此,OGG使用索引加快了数据同步delete速度。