排查逻辑Standby日志应用缓慢之Troubleshooting(原创)

Troubleshooting SQL*Loader Sessions
Oracle SQL*Loader provides a method of loading data from different sources into the Oracle Database. This section analyzes some of the features of the SQL*Loader utility as it pertains to the SQL Apply engine.
由于SQL*Loader同样采用SQL Apply engine的机制,故SQL*Loader脚本文件的不同也会影响到逻辑standby应用日志的速度。
我们知道SQL*Loader装载数据库时有如下3种方式。
LOAD DATA
INTO TABLE LOAD_STOK APPEND              #在
LOAD_STOK后追加数据
LOAD DATA
INTO TABLE LOAD_STOK REPLACE
             #在同一事务中,先delete LOAD_STOK中的数据后,导入数据
LOAD DATA
INTO TABLE LOAD_STOK TRUNCATE
          #先truncate LOAD_STOK,然后导入数据 ,需事先禁用相关约束,否则会报错
采用replace关键字比起truncate关键字更有可能引起逻辑备库上产生性能问题。

设想一个场景,如果主库上表A有10000条数据,当使用delete表A的数据时,主库能够对 表A执行delete语句将10000行数据库直接删除,但备库的SQL Apply engine需要对表A进行逐行删除,也就是执行10000次delete。如果表A上没有索引的话,那么就意味着备库需要对表A进行10000次全表扫 描。同时由于操作的行数较多,备库上对表A进行操作的事务将会被定义为"eager transaction"。 Eager Transaction是dml超过200行数据的事务,从10g R1开始,更新行数超过200行的事务都被定义为 Eager TransactionEager Transaction的目的主要是为了当超过200行的数据操作时,可以及时同步到备库中,而不必等待事务被完整建立(笔者个人看法,应该是当dml超过200行时,不必等待该dml操作结束即同步到备库上,待考证)。

因此如果采用SQL*Loader装载数据库时需要删除原表的数据,建议采用truncate关键字。
Troubleshooting Pageouts
逻辑备库需要将redo记录解析成LCR,会在shared pool里分配一部分空间来作为LCR Cache,如果cache太小,就会像OS的虚拟内存管理一样,需要做page out,这会严重影响应用日志的性能。默认情况下,LCR Cache为Shared pool的四分之一,最少不少于30M,否则SQL Apply不能启动。如果机器的内存足够,建议将LCR Cache尽量设大一点,当然,同时share pool也要足够大。如果机器内存有限,那可以考虑将buffer cache减少一点来给LCR Cache腾出空间。
当SQL Apply engine想要创建大事务时,将会先使用"lcr cache",而不是立即报类似于ORA-4031的错误。lcr cache的作用类似于操作系统上的swap,可以通过下列SQL查看是否发生了SQL Apply pageouts
SQL> select value
     from v$logstdby_stats
     where name like '%paged%';
VALUE
------------
358706176

上例中,SQL Apply engine启动后产生了358706176字节的换页。如果换页频繁出现在日常操作中,我们可以考虑加大逻辑备库的MAX_SGA
和操作系统的swap换页一样,SQL Apply engine会将换出的数据页写入到DBMS_LOGMNR_D.SET_TABLESPACE指定的表空间中,如果没有指定,默认为SYSAUX表空 间。oracle会在该表空间创建、命名lob段存储换出的数据页。如果指定的表空间没有足够的空余空间将会报ORA-1691错 误,alertSID.log里的报错如下
Tue Feb  8 06:23:22 2005
Errors in file /u01/app/oracle/admin/TPCC_BOSTON/bdump/tpcc1_lsp0_9052.trc:
ORA-12801: error signaled in parallel query server P001, instance haha5:TPCC1 (1)
ORA-01691: unable to extend lob segment SYSTEM.SYS_LOB0000005331C00008$$ by 128 in tablespace LOGMNR
Tue Feb  8 06:23:22 2005
TLCR process death detected. Shutting down TLCR
logminer process death detected, exiting logical standby

当出现ORA-1691错误时,可以考虑以下解决方式
1、分配更多的空间到指定表空间
2、扩大MAX_SGA
Troubleshooting Long Running Transactions

造成备库上长事务最普遍的原因之一是全表扫描,同样的创建或者重建索引也容易在备库上引起长事务。可以通过以下步骤来确认备库上是否出现长事务。

查看alert.log,如果SQL Apply engine执行某一SQL时间超过了阀值,则会在alert.log上看到如下信息
Mon Feb 17 14:40:15 2003
WARNING: the following transaction makes no progress
WARNING: in the last 30 seconds for the given message!
WARNING: xid =
0x0016.007.000017b6 cscn = 1550349, message# = 28, slavid = 1
knacrb: no offending session found (not ITL pressure)

注意,最后一行标示,长事务是由于全表扫描造成的,和ITL槽的争用无关。这一警告信息通常是在SQL执行超过30秒后出现
直接查找出引起长事务的sql比较困难,我们可以通过以下SQL定位数据库中正在操作的对象
SQL> SELECT SAS.SERVER_ID
          , SS.OWNER
          , SS.OBJECT_NAME
          , SS.STATISTIC_NAME
          , SS.VALUE
     FROM V$SEGMENT_STATISTICS SS
          , V$LOCK L
          , V$STREAMS_APPLY_SERVER SAS
     WHERE SAS.SERVER_ID = &SLAVE_ID
        AND L.SID = SAS.SID
        AND L.TYPE = 'TM'
        AND SS.OBJ# = L.ID1;

另外,可以通过以下SQL定位数据库中,磁盘读较多的SQL
SQL> SELECT SUBSTR(SQL_TEXT,1,40)
          , DISK_READS
          , EXECUTIONS
          , DISK_READS/EXECUTIONS
          , HASH_VALUE
          , ADDRESS
     FROM V$SQLAREA
     WHERE DISK_READS/GREATEST(EXECUTIONS,1) > 1
         AND ROWNUM < 10
     ORDER BY DISK_READS/GREATEST(EXECUTIONS,1) DESC;

通常建议备库中表需含有主健,这样就被定义未主健的列就会是NOT NULL的。如果没有合适的列作为主健,也应通过创建索引来使表包含NOT NULL列。以上两点的设计都是为了尽量减少全表扫描。如果表设计无法满足以上两点,应该重新确认表设计是否合理,或者在备库应用sql的时候跳过该对 象。具体方法如下:
SQL> alter database stop logical standby apply;
          Database altered.
SQL> execute dbms_logstdby.skip('DML','SCOTT','FTS');
          PL/SQL procedure successfully completed.
SQL> alter database start logical standby apply;
          Database altered

Troubleshooting ITL Pressure
如果alert.log中出现如下报错
Tue Apr 22 15:50:42 2003
WARNING: the following transaction makes no progress
WARNING: in the last 30 seconds for the given message!
WARNING: xid =
0x0006.005.000029fa cscn = 2152982, message# = 2, slavid = 17

上述信息表示, SQL Apply process (slaveid) #17在30秒内没有结束,可以通过下列SQL查询具体SQL
SQL> select sa.sql_text
     from v$sqlarea sa
          , v$session s
          , v$streams_apply_server sas
     where sas.server_id = &slaveid
        and s.sid = sas.sid
        and sa.address = s.sql_address;

SQL_TEXT
------------------------------------------------------------
insert into "APP"."LOAD_TAB_1" p("PK","TEXT")values(:1,:2)

也可以通过下列SQL来确定ITL是否存在压力
SQL> select sid,type,id1,id2,lmode,request
     from v$lock
     where type = 'TX';

    SID        TY ID1        ID2        LMODE      REQUEST
    ---------- -- ---------- ---------- ---------- ----------
             8 TX     327688         48          6          0
            10 TX     327688         48          0          4

通过上述查询可以看到,会话10的4(共享锁)正在等待会话8所持有的6(排他锁),lock-id (327688,48)。如果想查询未超过30秒的ITL等待,我们可以通过如下SQL

SQL> select owner, object_name, object_type, value
     from v$segment_statistics
     where statistic_name = 'ITL waits'
       and value > 0
     order by value;

该语句将会查询出自实例启动以来的所有ITL等待,该sql不局限于逻辑备库,适用于任何的oracle数据库。以下两种情况是引起ITL争用的主要原因:
1、主、备库块之间分布不均。以下是具体解释
Two rows that exist in different blocks at the primary database, and hence can be modified concurrently at the primary database, can reside on the same block and thus be susceptible to ITL pressure at the logical standby. Two rows residing on the same block that are modified serially at the primary database, may be modified concurrently at the logical standby database, because SQL Apply tries to do as much work as possible in a concurrent fashion.
2、 主、备库之间SQL的执行方式不一样。
我们可以通过设置INITRANS这个存储参数来缓解ITL槽的争用,可以通过下面的方法来确定 INITRANS值
找出每个数据块中大约有多少行数据

avg_row_in_block = average number of rows in a data block = (0.9 x size of data block) / (avg_row_len from dba_all_tables for the relevant table)
本例中通过如下查询确定数据行的长度:
SQL> select avg_row_len from dba_all_tables where owner = 'APP' and table_name = 'LOAD_TAB_1';
查看applyer进程的个数
SELECT COUNT(*) AS APPLIER_COUNT FROM V$LOGSTDBY_PROCESS WHERE TYPE = 'APPLIER';
通过下面的算法计算initrans
target #initrans = maximum (minimum (0.3 * number of apply servers, avg_row_in_block), 10);

由于上面的计算值为估算值,所以也需要为数据行增长保留一定的空间。一般我们可将 INITRANS设置为10,同时设置PCTFREE大于等于10(换句话说,在数据块中为行和ITL增长保留10%空余空间)。当然由于 INITRANS只对设置后的数据块生效,如果希望立即生效,则需要重组表。的 下面是具体实施步骤:
SQL> alter database stop logical standby apply;
      Database altered.
SQL> execute dbms_logstdby.guard_bypass_on;
      PL/SQL procedure successfully completed
SQL> alter table app.load_tab_1 initrans <#initrans>;
      Table altered
.
SQL> execute dbms_logstdby.guard_bypass_off;
      PL/SQL procedure successfully completed
SQL> alter database start logical standby apply;
      Database altered

如果在逻辑备库进行了上述改动的话,Oracle建议也在主库上进行改动,避免进行切换后报错。
Troubleshooting ORA-1403 errors with Flashback Transaction
通常情况下, ORA-1403在逻辑备库中并不会出现。只有当逻辑备库直接操作数据后,并且同样的数据也在主库上进行操作时才会出现 ORA-1403 报错 。我们可以利用闪回事务查询来构造备库丢失的数据,当然闪回事务查询能发成功取决于备库上的 undo_retention参数

当出现 ORA-1403报错时,会在备库上的 DBA_LOGSTDBY_EVENTS视图和alert.log中记录报错信息 。报错信息如下
LOGSTDBY stmt: update "SCOTT"."MASTER"
  set
    "NAME" = 'john'
  where
    "PK" = 1 and
    "NAME" = 'andrew' and
    ROWID = 'AAAAAAAAEAAAAAPAAA'
LOGSTDBY status: ORA-01403: no data found
LOGSTDBY PID 1006, oracle@staco03 (P004)
LOGSTDBY XID 0x0006.00e.00000417, Thread 1, RBA 0x02dd.00002221.10

遇到如下报错,我们可以通过步骤定位问题
1、查询出引起报错的版本
SQL> select versions_xid
      , versions_startscn
      , versions_endscn
      , versions_operation
      , pk
      , name
     from scott.master
        versions between scn minvalue and maxvalue
     where pk = 1
     order by nvl(versions_startscn,0);

VERSIONS_XID     VERSIONS_STARTSCN VERSIONS_ENDSCN V  PK NAME
---------------- ----------------- --------------- - --- -------
03001900EE070000           3492279         3492290 I   1 andrew
02000D00E4070000           3492290                 D   1 andrew

From the information returned, it can be seen that the record was first inserted at scn 3492279 and then was deleted at scn 3492290 as part of transaction ID 02000D00E4070000. Using the transaction ID, the database should be queried to find the scope of the transaction. This is achieved by querying the flashback_transaction_query view.
从上面的查询可以看到,
在事务号为02000D00E4070000 时,记录被删除,通过记录号,我们可以查询出undo sql。
SQL> select operation
       , undo_sql
     from flashback_transaction_query
     where xid = hextoraw('02000D00E4070000');

OPERATION  UNDO_SQL
---------- ------------------------------------------------
DELETE     insert into "SCOTT"."MASTER"("PK","NAME") values
           ('1','andrew');
BEGIN

执行上面的UNDO SQL即可
SQL> insert into "SCOTT"."MASTER"("PK","NAME") values ('1','andrew');
SQL> commit;

重新启动sql应用
SQL> alter database start logical standby apply;


参考至:《 Oracle10g Data Guard SQL Apply Troubleshooting [ID 312434.1]》
                https://support.oracle.com/CSP/ui/flash.html#tab=KBHome%28page=KBHome&id=%28%29%29,%28page=KBNavigator&id=%28userQuery=ID%20312434.1&startIndex=1&docsRequested=20&back=true&defaultSearch=true&facetIdClicked=&powerview_search=&cmd=getAllNodes&enterprise=true&clickstreamSource=Bookmark&requestedFacets=0&filterSource=KB%29%29

本文原创,转载请注明出处、作者

如有错误,欢迎指正

邮箱:[email protected]

猜你喜欢

转载自czmmiao.iteye.com/blog/1462941