实时集成Oracle CDC相关问题

以下为您介绍实时集成相关的常见问题和解决方案。

Dataphin实时集成使用的Oracle CDC版本?

Dataphin V3.13版本前实时集成使用的Oracle CDC 2.3版本;Dataphin V3.13版本后实时集成使用的Oracle CDC 2.4版本。

任务运行报错:Oracle LogMiner不支持在PDB,可插拔数据库(Pluggable Databases)上执行?

在CDB数据库中,读取PDB的补偿日志,需要额外配置debezium.database.pdb.name参数。

任务运行报错:ORA-00942: table or view does not exist。

Oracle CDC 2.4的一个bug。产生的底层原因是UPDATE LOG_MINING_FLUSH SET LAST_SCN =。更多信息,请参见oracle-cdc cannot read oracle multitenant pdb binlog

您需要手动去CDB的数据库中,切换到CDB实例,将LOG_MINING_FLUSH进行重建。详情如下:

-- 切换到CDB实例。
ALTER SESSION SET CONTAINER = CDB$ROOT;

-- 建表并写入数据。
CREATE TABLE LOG_MINING_FLUSH(LAST_SCN NUMBER(19,0));
INSERT INTO LOG_MINING_FLUSH VALUES (0);

任务运行报错:表名长度超过30个字符(Table 'xxx' won't be captured by Oracle LogMiner because its name exceeds 30 characters)

对于需要采集的表,请限制表名和列名都<=30个字符。主要原因为Oracle LogMiner的限制。更多信息,请参见LogMiner Requirements

运行日志报错:DataException: file is not a valid field name。

您需要使Flink中字段的名字大小写和Oracle上的表完全一样。Oracle LogMiner日志中有数据的字段的名字是file,没有定义在Oracle LogMiner的表的schema中,导致找不到列而报错。

日志能读取到Oracle的Oracle LogMiner数据,但是无法读取到数据。

主要是因为Oracle CDC的架构问题而导致读取效率比较低,建议使用Dataphin实时集成。实时集成在部分大数据量的场景下,性能表现良好。具体原理如下:

  1. 获取每次读取的startScnendScn

  2. 找出包含startScn的所有的archiveLogonlineLog文件(>=startScn都会加载)。

  3. 调用Oracle LogMiner分析这些文件,使用DBMS_LOGMNR.START_LOGMNR进行分析(这个步骤很慢)。

  4. V$LOGMNR_CONTENTS中读取分析后的CDC数据(这个读取步骤也比较慢)。

当数据量比较大的时候,会导致Oracle CDC读取的数据更慢(因为存在数据重复分析)。而且Oracle CDC使用了在线字典,会有额外的资源开销。

附录:问题排查

排查方法

当以上常见问题无法解决时,您可以通过以下方法进行排查。

Oracle CDC很多问题都是权限导致的,首先确保一定按照自己的数据库(CDB容器数据库(Container Database)模式、非CDB模式)环境,配置好Oracle的配置。更多信息,请参见Connector properties

说明

Oracle CDC的权限项目比较多,CDB模式和非CDB模式的权限有不同,请先按照文档配置。

查询用户的权限使用如下命令:

-- 查询当前账户:
select user from dual;

SELECT * FROM DBA_SYS_PRIVS WHERE GRANTEE = 'C##DBZUSER';
SELECT TABLE_NAME FROM DBA_TAB_PRIVS WHERE GRANTEE = 'C##DBZUSER';

排查命令

常用命令

-- 切换CDB和PDB
ALTER SESSION SET CONTAINER = CDB$ROOT;

-- 查询当前用户
select user from dual;

-- 查询补偿日志是否开启
select * from dba_log_groups where table_name = 'PF_ID_NAME_PK';

-- 查询用户权限
SELECT * FROM DBA_SYS_PRIVS WHERE GRANTEE = 'C##DBZUSER';
SELECT TABLE_NAME FROM DBA_TAB_PRIVS WHERE GRANTEE = 'C##DBZUSER';

-- 获取当前scn
SELECT CURRENT_SCN FROM V$DATABASE

补偿日志分析

-- 分析补偿日志
BEGIN
  DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '/opt/oracle/product/19c/dbhome_1/dbs/arch1_83_1158061769.dbf', OPTIONS => DBMS_LOGMNR.NEW);
  DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '/opt/oracle/oradata/ORCLCDB/redo01.log', OPTIONS => DBMS_LOGMNR.ADD_FILE);
  DBMS_LOGMNR.START_LOGMNR(Options => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);
END;

-- 停止日志分析
exec DBMS_LOGMNR.END_LOGMNR();

-- 查询补偿日志内容,里面会有变更记录
SELECT SEG_NAME,TIMESTAMP,OPERATION FROM V$LOGMNR_CONTENTS WHERE SEG_NAME = 'PF_ID_NAME_PK' ORDER BY TIMESTAMP;

补偿日志文件名获取

-- 查看online log文件名
查看logfile文件名:
SELECT V$LOGFILE.MEMBER                                                                                  NAME,
       V$LOG.THREAD#                                                                                     THREAD_NUMBER,
       V$LOG.SEQUENCE#                                                                                   SEQUENCE_NUMBER,
       V$LOG.FIRST_CHANGE#                                                                               FIRST_CHANGE_NUMBER,
       LEAD(V$LOG.FIRST_CHANGE#, 1, 281474976710655)
            OVER (ORDER BY V$LOG.SEQUENCE#)                                                              NEXT_CHANGE_NUMBER,
       TO_CHAR(V$LOG.FIRST_TIME, 'YYYY-MM-DD HH24:MI:SS')                                                FIRST_TIME,
       TO_CHAR(LEAD(V$LOG.FIRST_TIME, 1, NULL) OVER (ORDER BY V$LOG.SEQUENCE#), 'YYYY-MM-DD HH24:MI:SS') NEXT_TIME,
       0                                                                                                 BLOCK_SIZE,
       V$LOG.BYTES                                                                                       BYTES,
       V$LOG.GROUP#                                                                                      GROUP_NUMBER,
       V$LOG.MEMBERS                                                                                     MEMBERS,
       V$LOG.ARCHIVED                                                                                    ARCHIVED,
       V$LOG.STATUS                                                                                      STATUS
FROM V$LOG,
     V$LOGFILE
WHERE (V$LOG.STATUS = 'CURRENT' OR V$LOG.STATUS = 'ACTIVE' OR V$LOG.STATUS = 'INACTIVE')
  AND V$LOG.GROUP# = V$LOGFILE.GROUP#
  AND V$LOG.THREAD# = 1
ORDER BY V$LOG.SEQUENCE#;

-- 查询archive log
SELECT NAME,
       THREAD#                                      THREAD_NUMBER,
       SEQUENCE#                                    SEQUENCE_NUMBER,
       FIRST_CHANGE#                                FIRST_CHANGE_NUMBER,
       NEXT_CHANGE#                                 NEXT_CHANGE_NUMBER,
       TO_CHAR(FIRST_TIME, 'YYYY-MM-DD HH24:MI:SS') FIRST_TIME,
       TO_CHAR(NEXT_TIME, 'YYYY-MM-DD HH24:MI:SS')  NEXT_TIME,
       BLOCK_SIZE,
       BLOCKS,
       DEST_ID,
       RESETLOGS_ID,
       RESETLOGS_CHANGE#                            RESETLOGS_CHANGE_NUMBER,
       RESETLOGS_TIME,
       STATUS,
       CREATOR,
       REGISTRAR,
       APPLIED,
       FAL,
       DELETED
FROM V$ARCHIVED_LOG
WHERE NAME IS NOT NULL
  AND STATUS = 'A'
  AND DELETED = 'NO'
  AND ARCHIVED = 'YES'
  AND STANDBY_DEST = 'NO'
-- AND THREAD# = 1
-- AND RESETLOGS_ID = 1158061769
  AND FIRST_TIME <= TO_TIMESTAMP('2024-02-22 11:30:00', 'yyyy-MM-dd hh24:mi:ss')
  AND NEXT_TIME > TO_TIMESTAMP('2024-02-22 12:00:00', 'yyyy-MM-dd hh24:mi:ss');