3.6 如何保证数据一致性

保证迁移前后的数据一致性是一件至关重要的事,如果这一条底线都无法满足,别的就更无从谈起了。百分之八九十的物理迁移都能保证数据一致性,但也不排除因为内部一些自动任务或外部连接所引起的数据变更,而导致的数据不一致。相较于物理迁移,逻辑迁移导致数据不一致的概率则要高很多。本着对客户负责的原则,DBA如何用事实来证明迁移前后的数据一致性是本节讨论的重点。

接下来,我们将介绍三种检查数据一致性的方法。

1.Oracle GoldenGate Veridata

Veridata不仅可用于检查数据的不一致,而且能在数据不一致的情况下进行修复,关于Veridata的具体介绍和配置方法请参考3.5.3节,下面就来演示如何使用Veridata修复不一致的数据。图3-3所示的是作业jtest的比对情况,虽然作业已成功执行,但存在数据不一致的问题。

当检测到数据不同步时,Veridata将报告出数据不一致的情况,并提供修复选项。查看jtest的比对结果,我们可以看到有一张表的数据不同步。可以通过单击该作业运行的“Compare Pairs Out-Of-Sync”中的超链接来查看不同步的详细信息,超链接的具体信息如图3-4所示。

如图3-5所示,Veridata已检测到不同步的情况。对于修复,这里提供了两种方法,分别为生成修复SQL和直接在页面上修复。

图3-3 作业完成后的比对情况

图3-4 表数据不同步的具体情况

图3-5 不同步数据的修复方法

如果点击“Generate SQL”,则会在Veridata服务器/tmp目录下生成一个以作业名为前缀的压缩(格式为zip)包。


shell> ls -rtl jtest*
-rw-r----- 1 oracle oinstall 23710 Jul 25 09:43 jtest1595641348119.zip

修复时只需要将解压出来的SQL文本传至目标端数据库执行即可。

另一种方法是直接单击“run repair”(运行修复),修复完成后,状态(STATUS)变为绿色,如图3-6所示。

图3-6 不同步数据修复后的情况

查看最新的同步状态,建议重新执行一次作业,以便再次获取同步报告。

如果无法使用Veridata,我们也可以使用行数对比和DBMS_COMPARE包的方式,但这两种方式在遇到数据不一致问题的时候需要手动处理。

2.行数对比

在数据准确性方面,行数对比方式没有其他两种方式高,如果系统繁忙,那么行数差异的问题永远都会存在。

使用这种方法需要在系统用户下创建对比表格,定义如下:


SQL> CREATE TABLE ODC.ROW_COUNT_STATS 
 (SCHEMANAME VARCHAR2(30 BYTE), 
 TABLENAME VARCHAR2(30 BYTE), 
 ROW_CNT_SOURCE NUMBER, 
 ROW_CNT_TARGET NUMBER, 
 CNT_DIFF NUMBER);

还需要额外创建连接源端的DBLINK,命令如下:


SQL> CREATE PUBLIC DATABASE LINK OGGLINK
CONNECT TO ODC IDENTIFIED BY odc
USING '(DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.238.131)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = joe)
    )
  )';

之后将需要比对的用户和表插入新创建的TABLE_LIST表中,命令如下:


SQL> CREATE TABLE ODC.TABLE_LIST 
 (SCHEMANAME VARCHAR2(30 BYTE), 
 TABLENAME VARCHAR2(30 BYTE));

<!--插入对比表格-->
SQL> select * from ODC.TABLE_LIST;
SCHEMANAME                     TABLENAME
------------------------------ ------------------------------
SCOTT                          TEST1
SCOTT                          SALGRADE
SCOTT                          BONUS
SCOTT                          EMP
SCOTT                          DEPT

使用下面的命令比对数据是否一致:


SQL> declare
  v_schemaname varchar2(60);
  v_tablename  varchar2(60);
  v_tarcount   NUMBER(16) := 0; 
  v_srccount   NUMBER(16) := 0; 
  v_sql1       varchar2(2000);
  v_sql2       varchar2(2000);
  v_sql3       varchar2(2000);
  v_cntdiff    NUMBER(16) := 0; 
  
  cursor cur_tablist is
    select SCHEMANAME,TABLENAME
      from ODC.TABLE_LIST;
begin
  open cur_tablist;
  loop
    fetch cur_tablist
    into v_schemaname,v_tablename;
    exit when cur_tablist%notfound;
    v_sql1 := 'select count(*) from ' || v_schemaname ||'.'|| v_tablename || '';
    execute immediate v_sql1 into v_tarcount;
    v_sql2 := 'select count(*) from ' || v_schemaname ||'.'|| v_tablename || '@OGGLINK';
    execute immediate v_sql2 into v_srccount;
    v_cntdiff :=v_tarcount - v_srccount;
    v_sql3 := 'insert into ODC.ROW_COUNT_STATS (schemaname,tablename,
    row_cnt_source,row_cnt_target,cnt_diff) values ('''||upper(v_
    schemaname)||''','''||v_tablename||''',' || v_srccount || ',' || v_tarcount 
    || ',' || v_cntdiff || ')';  
    execute immediate v_sql3; 
  end loop;
  close cur_tablist;
end;
/

完成后,可以在ROW_COUNT_STATS表中生成报告,以确定是否存在差异。根据该表的CNT_DIFF列结果进行判断,如果是0,则表示数量一致,如果不为0,则表示两端数据存在差异。示例代码如下:


SQL> select * from ODC.ROW_COUNT_STATS ;
SCHEMANAME      TABLENAME  ROW_CNT_SOURCE ROW_CNT_TARGET   CNT_DIFF
--------------- ---------- -------------- -------------- ----------
SCOTT           TEST1               86507          86507          0
SCOTT           SALGRADE                5              5          0
SCOTT           BONUS                   0              0          0
SCOTT           EMP                    14             14          0
SCOTT           DEPT                    4              4          0

3.使用DBMS_COMPARE包

执行数据验证的另一种替代方法是,使用数据库内置的DBMS_COMPARE包,此方法使用哈希逐行比对,速度上没有其他两种方式快,而且无法用于LOB(大对象)数据类型和没有主键的表。有关DBMS_COMPARE的其他限制,可以查阅Oracle官方文档《Database PL/SQL Packages and Types Reference》获取更多信息。

DBMS_COMPARE包进行数据验证的具体使用步骤如下。

1)创建比较任务。

2)执行比较任务。

3)手动修复不一致的数据。

此处仅比较scott用户下的emp表,创建任务的命令如下:


SQL> BEGIN 
 DBMS_COMPARISON.CREATE_COMPARISON( 
 comparison_name => 'SCOTT_EMP_COMPARE', 
 schema_name => 'SCOTT', 
 object_name => 'EMP', 
 dblink_name => 'OGGLINK'); 
END; 
/

上述代码段中的参数说明如下。

·comparison_name:自定义对比名称。

·dblink_name:目标端与源端数据库连接。

带入创建的自定义名称SCOTT_EMP_COMPARE,执行比较任务,命令如下:


SQL> SET SERVEROUTPUT ON 
SQL> DECLARE 
 consistent BOOLEAN; 
 compare_info DBMS_COMPARISON.COMPARISON_TYPE; 
BEGIN 
 consistent := DBMS_COMPARISON.COMPARE( 
 comparison_name => 'SCOTT_EMP_COMPARE', 
 scan_info => compare_info, 
 perform_row_dif => TRUE 
 ); 
 DBMS_OUTPUT.PUT_LINE('Scan ID: '||compare_info.scan_id); 
 IF consistent=TRUE THEN 
 DBMS_OUTPUT.PUT_LINE('The table is equivalent'); 
 ELSE 
 DBMS_OUTPUT.PUT_LINE('Tables are not equivalent… there is data divergence.'); 
DBMS_OUTPUT.PUT_LINE('Check the dba_comparison and dba_comparison_scan_summary 
    views for 
locate the differences for compare_id:'||compare_info.scan_id); 
 END IF; 
END; 
/
Scan ID: 1
Tables are not equivalent there is data divergence.
Check the dba_comparison and dba_comparison_scan_summary views for
locate the differences for compare_id:2

在上述代码中,not equivalent程序包检测到数据差异,并记录到系统视图DBA_COMPARISON、DBA_COMPARISON_SCAN_SUMMARY和DBA_COMPARISON_ROW_DIF中。接下来查询DBA_COMPARISON_ROW_DIF,以确认不同步的数据(如图3-7所示)。

图3-7 整体查询不同步的数据

然后,通过图3-7中返回的rowid查询具体的数据,结果如图3-8所示。

图3-8 具体查询不同步的数据

查明差异行数据之后,最后一步就是修复了,我们可以在删除目标端差异数据之后再重新插入,实现方法有很多种,这里就不做展开了,大家可以根据自身的实际情况选择以上三种方式中的任意一种,强烈建议选择第一种方式,因为不管是准确性还是效率,Oracle GoldenGate Veridata都是最高的。