一、创建DB_LINK
1、Oracle验证MySQL驱动
注意:Oracle数据库在安装过程中会自动安装MySQL的驱动"dg4odbc"。目前仅需要验证该驱动即可。当前环境中存在该驱动。
bash
$ file $ORACLE_HOME/bin/dg4odbc
2、配置驱动连接
注意:此处需要在Oracle数据库中配置驱动连接文件。同时,MySQL数据库是严重区分大小写,需要注意驱动文件中的大小敏感。
bash
$ vi $ORACLE_BASE/odbc.ini
[myodbc5]
Driver = /home/dbs/app/myodbc-x.x.x/lib/libmyodbc5.so
Description = Connector/ODBC x.x Driver DSN
SERVER = <IP ADDRESS>
PORT = <MYSQL PORT NUMBER>
USER = mysql_user
PASSWORD = *****
DATABASE = test
OPTION = 0
TRACE = OFF
3、配置监听
注意:此处需要在listener.ora和tnsnames.ora文件中均需要配置MYSQL监听。
bash
# tnsname.ora文件
$ vi $ORACLE_HOME/rdbms/admin/[SID]/tnsnames.ora
myodbc5 =
(DESCRIPTION=
(ADDRESS=
(PROTOCOL=TCP) (HOST=[MYSQL HOST]) (PORT=1521)
)
(CONNECT_DATA=
(SID=myodbc5))
(HS=OK)
)
# listener.ora文件
$ vi $ORACLE_HOME/network/admin/listener.ora
SID_LIST_LISTENER=
(SID_LIST=
(SID_DESC=
(SID_NAME=myodbc5)
(ORACLE_HOME=/home/dbs/app/Ora/product/11.2.0/dbhome_1)
(PROGRAM=dg4odbc)
(ENV="LD_LIBRARY_PATH=/home/dbs/app/unixodbc-2.2.14/lib:/home/dbs/app/Ora/product/11.2.0/dbhome_1/lib")
)
)
4、配置透明网关
注意:此处网关为数据库的,不是OS的网关
bash
$ vi $ORACLE_HOME/hs/admin/initmyodbc5.ora
HS_FDS_CONNECT_INFO=myodbc5
# Data source name in odbc.ini
HS_FDS_TRACE_LEVEL=OFF -- 关闭追踪,降低负载,如出现错误,可以开启追踪,日志文件在$ORACLE_HOME/hs/ 下
HS_FDS_SHAREABLE_NAME=/home/dbs/app/unixodbc-2.3/lib/libodbc.so ß 系统中该文件的路径
HS_FDS_SUPPORT_STATISTICS=FALSE
HS_LANGUAGE= AMERICAN_ AMERICA. AL32UTF8
AMERICAN_AMERICA.WE8ISO8859P15ß Oracle建议当前值,但也可以输入Oracle字符集,原因是ODBC可能不识别字符集
#
# ODBC env variables
set ODBCINI=/home/dbs/etc/odbc.ini
5、重载监听
bash
$ lsnrctl reload -- 重新加载
6、创建DB_LINK
注意:myodbc5是tnsnames.ora文件中的监听服务名
bash
SQL> create public database link OM_LINK connect to "XXXX" identified by "XXXXXX" using'myodbc5';
7、测试连接即可
SQL> select * from "tab1"@myodbc5;
二、新建同步测试案例
1、建立同步表(Oracle数据库)
注意:需要从MySQL同步数据过来,建立对应的字段,并且设置好数据类型即可。当前MySQL和Oracle的字符集分别是"utf8mb4"和"AMERICAN_ AMERICA. AL32UTF8"
bash
SQL>create table test_t1(id integer,create_date date);
2、新建同步记录表
注意:因为数据是不断变化的,在做增量同步时,需要考虑一个关键值且该值时有规律的变化或者主键ID、时间戳都可以。当前经了解其ID是使用系统自增的,那么可以通过ID来判断哪些数据是新增的,将最后同步的最大的ID写到同步记录表中,下次自动同步时获取该值与MySQL数据库中的表进行比对,比该值小的及同步到Oracle数据库中。
bash
create table om_sync_control (
table_name varchar2(50) primary key, -- 被同步的表名
last_sync_id number(18), -- 上次同步的最大ID
last_sync_time date, -- 上次同步时间
sync_status varchar2(10) -- 状态:SUCCESS/FAILED
);
3、创建错误信息表
注意:该表用来记录每次同步数据时是否发生报错,如果出现报错将记录对应的报错信息
bash
CREATE TABLE om_sync_error_log (
table_name VARCHAR2(50), -- 被同步的表名
ids NUMBER(20), -- 失败的ID
om_error BLOB, -- 错误原因
insert_date DATE -- 插入日期
);
4、初始化同步记录表
bash
SQL> insert into om_sync_control (table_name, last_sync_id, last_sync_time, sync_status) values ('test_t1', 0, sysdate, 'SUCCESS');
5、创建存储过程
bash
-- 创建同步数据存储过程
create or replace procedure SYNC_ON_INCREMENTAL is
v_max_id number := 0; -- 记录最大值
v_count number := 0; -- 插入数据总量
v_last_sync_id number; -- 上次同步的最大值
v_sync_time date := sysdate; -- 同步时间
-- 自定义异常
e_sync_error exception
-- 游标:查询上次同步后新增的数据
cursor new_data_cur is
select id, create_date
from test_t1@OM_LINK
where id > (select nvl(last_sync_id, 0)
from om_sync_control
where table_name = 'test_t1')
order by id; -- 按ID排序,确保顺序处理
begin
-- 获取当前已同步的最大ID(用于后续更新)
SELECT
nvl(last_sync_id, 0)
INTO v_last_sync_id
FROM
om_sync_control
WHERE
table_name = 'test_t1'
FOR UPDATE; -- 加锁,防止并发同步
dbms_output.put_line('开始增量同步,上次同步ID:' || v_last_sync_id);
-- 遍历新增数据
for rec in new_data_cur loop
begin
-- 插入到Oracle目标表
insert into test_t2 (
id,
create_date
) values (
rec.id, -- 使用MySQL的自增ID
rec.create_date
);
-- 记录本次循环的最大ID
if rec.id > v_max_id then
v_max_id := rec.id;
end if;
v_count := v_count + 1;
-- 每100条提交一次,避免大事务
if mod(v_count, 100) = 0 then
commit;
dbms_output.put_line('已同步 ' || v_count || ' 条,当前最大ID:' || v_max_id);
end if;
exception
when others then
-- 记录错误信息,继续处理下一条
dbms_output.put_line('插入失败,ID=' || rec.id || ',错误:' || sqlerrm);
--insert into om_sync_error_log values('test_t1',rec.id,'error',sysdate);
insert into om_sync_error_log values('test_t1',rec.id,'sqlerrm',sysdate);
commit;
end;
end loop;
-- 更新同步记录表
if v_max_id > 0 then
update om_sync_control
set last_sync_id = v_max_id,
last_sync_time = v_sync_time,
sync_status = 'SUCCESS'
where table_name = 'test_t1';
commit;
dbms_output.put_line('增量同步完成。共同步 ' || v_count || ' 条新数据,最大ID:' || v_max_id);
else
dbms_output.put_line('没有新数据需要同步。');
end if;
exception
when others then
rollback;
dbms_output.put_line('同步过程出错:' || sqlerrm);
-- 更新状态为失败
update om_sync_control
set sync_status = 'FAILED',
last_sync_time = sysdate
where table_name = 'test_t1';
commit;
raise;
end SYNC_ON_INCREMENTAL;
6、配置定时任务
bash
-- 开启定时任务
BEGIN
dbms_scheduler.create_job(
job_name=>'SYNC_ON_INCREMENTAL_FIRST', --定时任务名称
job_type => 'STORED_PROCEDURE',--类型为存储过程
job_action=>'SYNC_ON_INCREMENTAL',--存储过程名称
start_date => sysdate, --首次执行时间
repeat_interval=>'FREQ=HOURLY;INTERVAL=12', --十二小时执行一次
--【FREQ=minutely; 表示间隔单位,可选值Yearly,monthly,weekly,datly,hourly,minutely,secondly】
enabled=>true -- 立即启用定时任务
);
END;
7、查看定时任务是否启动
- ENABLE:TRUE OR FALSE(标志定时任务是否启动)
- STATE:RUNNING(标志定时任务运行中)、SCHEDULED(定时中)
bash
-- 查询定时任务是否启动
Select * from dba_scheduler_jobs where job_name ='SYNC_ON_INCREMENTAL_FIRST' order by start_date desc;
8、查看定时任务具体执行信息
- STATUS:SUCCEEDED和FAILED(定时任务执行成功或失败)
- REQ_START_DTAE:定时任务执行时间
- ERRORS:记录系统报错信息,如:违反唯一约束条件、程序包或函数无效等
- OUTPUT:输出存储过程里面的结束时定义的提示语
bash
select * from dba_scheduler_job_run_details where job_name ='SYNC_ON_INCREMENTAL_FIRST' order by log_date desc;
9、关闭定时任务
bash
begin
dbms_scheduler.drop_job('SYNC_ON_INCREMENTAL_FIRST',force => true); -- 删除定时任务,force:无论当前定时任务什么状态,都删除
--dbms_scheduler.run_job('TEST_JOB4',true); -- 手动运行定时任务
--dbms_scheduler.enable('TEST02'); -- 启用定时任务,根据定义的执行频率,Oracle会自动决定什么时候开始运行
--dbms_scheduler.disable('TEST_JOB2');
end;
三、案例参考
注意:当前案例最重要的是体现当前方案的处理思路以及结果验证。但没有使用DB_LINK。
bash
-- 创建序列
CREATE SEQUENCE test_oracle_mysql
START WITH 1
INCREMENT BY 1
NOCACHE;
--建立测试表
create table test_t1(id integer,create_date date);
create table test_t2(id integer,create_date date);
-- 创建同步记录表
create table om_sync_control (
table_name varchar2(50) primary key, -- 被同步的表名
last_sync_id number(18), -- 上次同步的最大ID
last_sync_time date, -- 上次同步时间
sync_status varchar2(10) -- 状态:SUCCESS/FAILED
);
select * from om_sync_error_log;
-- 创建错误信息表
CREATE TABLE om_sync_error_log (
table_name VARCHAR2(50), -- 被同步的表名
ids NUMBER(20), -- 失败的ID
om_error BLOB, -- 错误原因
insert_date DATE -- 插入日期
);
drop table om_sync_error_log;
-- 初始化记录(第一次同步前执行)
insert into om_sync_control (table_name, last_sync_id, last_sync_time, sync_status)
values ('test_t1', 0, sysdate, 'SUCCESS');
--建立原表存储过程,插入测试数据
create or replace procedure test_om is
begin
--insert into test_om_consum select * from consum_ae_test;
insert into test_t1(id,create_date) values(test_oracle_mysql.nextval,sysdate);
commit;
end test_om;
-- 定时增加原表测试数据
BEGIN
dbms_scheduler.create_job(
job_name=>'test_job_om', --定时任务名称
job_type => 'STORED_PROCEDURE',-- 类型为存储过程
job_action=>'test_om',-- 存储过程名称
start_date => sysdate, --执行时间
--end_date => SYSDATE + 3 / 1440,
--repeat_interval=>'FREQ=MINUTELY;INTERVAL=1', --可选,执行频率,为空时表示只执行一次
--【FREQ=minutely; 表示间隔单位,可选值Yearly,monthly,weekly,datly,hourly,minutely,secondly】
enabled=>true); -- 立即执行
END;
-- 数据查询
select * from test_t1 order by id desc;
select * from test_t2 order by id desc;
select max(id) from test_t1;
select max(id) from test_t2;
select * from om_sync_control;
select * from dba_db_links;
-- 创建同步数据存储过程(目前暂未使用db-link,先把整个流程跑通,仅缺少新建DB-LINK)
create or replace procedure SYNC_ON_INCREMENTAL is
v_max_id number := 0; -- 记录最大值
v_count number := 0; -- 插入数据总量
v_last_sync_id number; -- 上次同步的最大值
v_sync_time date := sysdate; -- 同步时间
-- 自定义异常
e_sync_error exception;
-- 游标:查询上次同步后新增的数据
cursor new_data_cur is
select id, create_date
from test_t1
where id > (select nvl(last_sync_id, 0)
from om_sync_control
where table_name = 'test_t1')
order by id; -- 按ID排序,确保顺序处理
begin
-- 获取当前已同步的最大ID(用于后续更新)
SELECT
nvl(last_sync_id, 0)
INTO v_last_sync_id
FROM
om_sync_control
WHERE
table_name = 'test_t1'
FOR UPDATE; -- 加锁,防止并发同步
dbms_output.put_line('开始增量同步,上次同步ID:' || v_last_sync_id);
-- 遍历新增数据
for rec in new_data_cur loop
begin
-- 插入到Oracle目标表
insert into test_t2 (
id,
create_date
) values (
rec.id, -- 使用MySQL的自增ID
rec.create_date
);
-- 记录本次循环的最大ID
if rec.id > v_max_id then
v_max_id := rec.id;
end if;
v_count := v_count + 1;
-- 每100条提交一次,避免大事务
if mod(v_count, 100) = 0 then
commit;
dbms_output.put_line('已同步 ' || v_count || ' 条,当前最大ID:' || v_max_id);
end if;
exception
when others then
-- 记录错误信息,继续处理下一条
dbms_output.put_line('插入失败,ID=' || rec.id || ',错误:' || sqlerrm);
--insert into om_sync_error_log values('test_t1',rec.id,'error',sysdate);
insert into om_sync_error_log values('test_t1',rec.id,'error',sysdate);
commit;
end;
end loop;
-- 更新同步记录表
if v_max_id > 0 then
update om_sync_control
set last_sync_id = v_max_id,
last_sync_time = v_sync_time,
sync_status = 'SUCCESS'
where table_name = 'test_t1';
commit;
dbms_output.put_line('增量同步完成。共同步 ' || v_count || ' 条新数据,最大ID:' || v_max_id);
else
dbms_output.put_line('没有新数据需要同步。');
end if;
exception
when others then
rollback;
dbms_output.put_line('同步过程出错:' || sqlerrm);
-- 更新状态为失败
update om_sync_control
set sync_status = 'FAILED',
last_sync_time = sysdate
where table_name = 'test_t1';
commit;
raise;
end SYNC_ON_INCREMENTAL;
-- 开启定时任务
BEGIN
dbms_scheduler.create_job(
job_name=>'SYNC_ON_INCREMENTAL_FIRST', --必选 任务名称
job_type => 'STORED_PROCEDURE',--必须 任务类型【存储过程,匿名块等】
job_action=>'SYNC_ON_INCREMENTAL',--必须 任务内容,与job_type配合使用
start_date => sysdate, --可选 首次执行时间,为空时表示立即执行
-- end_date => SYSDATE + 5 / 1440,
repeat_interval=>'FREQ=MINUTELY;INTERVAL=2', --可选,执行频率,为空时表示只执行一次
--【FREQ=minutely; 表示间隔单位,可选值Yearly,monthly,weekly,datly,hourly,minutely,secondly】
enabled=>true); --可选 是否启用任务,注意:这里是enabled;
END;
-- 查询定时任务执行具体信息
select * from dba_scheduler_job_run_details where job_name ='SYNC_ON_INCREMENTAL_FIRST' order by log_date desc;
-- 查询定时任务是否启动
select * from dba_scheduler_jobs where job_name ='SYNC_ON_INCREMENTAL_FIRST' order by start_date desc;
-- 删除、运行、启动、关闭定时任务
begin
dbms_scheduler.drop_job('SYNC_ON_INCREMENTAL_FIRST',force => true); -- 删除定时任务,force:无论当前定时任务什么状态,都删除
--dbms_scheduler.run_job(' SYNC_ON_INCREMENTAL_FIRST ',true); -- 手动运行定时任务
--dbms_scheduler.enable(' SYNC_ON_INCREMENTAL_FIRST '); -- 启用定时任务,根据定义的执行频率,Oracle会自动决定什么时候开始运行
--dbms_scheduler.disable(' SYNC_ON_INCREMENTAL_FIRST ');
end;