生产环境禁用dblink全量覆盖,必须采用带增量逻辑、断点续传和错误恢复的方案;dblink需建为public且密码加双引号;同步须依赖时间戳或自增主键切片;定时任务推荐dbms_scheduler并配置超时参数。

直接用 DBLINK 在存储过程中做全量覆盖(比如先 DELETE 再 INSERT ... SELECT)看似简单,但生产环境基本不能用——锁表时间长、网络中断就失败、无法感知源库变更点。真正可用的方案必须带增量逻辑和错误恢复能力。
DBLINK 创建时必须用 PUBLIC 且密码要加双引号
非 PUBLIC 的 DBLINK 只对创建者用户可见,而定时任务(如 DBMS_JOB 或 DBMS_SCHEDULER)通常以系统用户或专用作业用户身份运行,权限不继承。密码不加双引号在 Oracle 12c+ 版本中极易报 ORA-01017: invalid username/password,哪怕账号密码完全正确——这是大小写敏感策略导致的默认行为。
- 正确写法:
create public database link MY_LINK connect to "scott" identified by "Tiger123" using '192.168.5.10:1521/ORCL'; - 测试连通性别只查
SELECT * FROM DUAL@MY_LINK,要真查一张小表,比如SELECT COUNT(1) FROM USER_TABLES@MY_LINK,否则可能掩盖权限不足问题 - 如果目标库启用了 TNS alias 解析,
USING后可直接写别名(如'ORCL_REMOTE'),但该别名必须在目标库所在服务器的$ORACLE_HOME/network/admin/tnsnames.ora中定义
存储过程里不能只写 INSERT/DELETE,得有增量判断依据
全量同步在表数据量超过 10 万行后,单次执行就可能超时或阻塞业务。必须依赖源表上的时间戳字段(如 LAST_MODIFIED)或自增主键(ID)来切片拉取。没有这类字段的表,得先加 ADD COLUMN LAST_MODIFIED DATE DEFAULT SYSDATE 并补全历史值,否则增量逻辑无从谈起。
- 推荐结构:用
SELECT MAX(LAST_MODIFIED) INTO v_max_time FROM SYNC_LOG先查本地上次同步时间点,再SELECT ... FROM SOURCE_TABLE@MY_LINK WHERE LAST_MODIFIED > v_max_time - 避免用
SYSDATE - 1/24这类硬编码窗口,它无法处理作业延迟、源库写入堆积等情况 - 插入前加
MERGE或先UPDATE再INSERT /*+ APPEND */,防止主键冲突;若目标表无主键,至少加NOT EXISTS子查询去重
DBMS_JOB 提交任务时 interval 表达式容易写错单位
sysdate + 1 是 1 天,sysdate + 1/24 是 1 小时,sysdate + 1/1440 是 1 分钟——但频繁轮询(如每分钟)会持续占用连接池、刷重做日志、拖慢源库性能。实际应按业务容忍延迟反推间隔,比如允许 5 分钟延迟,就设 sysdate + 5/1440,并配合 DBMS_LOCK.SLEEP(10) 避免空跑。
- 更稳妥的做法是改用
DBMS_SCHEDULER,支持失败自动重试、运行日志归档、资源组限制,例如:repeat_interval => 'FREQ=MINUTELY; INTERVAL=5' - 提交后务必查
SELECT JOB_NAME, STATE, LAST_START_DATE, NEXT_RUN_DATE FROM USER_SCHEDULER_JOBS确认状态为ENABLED,不是DISABLED或BROKEN - 不要在存储过程中调用
COMMIT后立刻DBMS_JOB.REMOVE,这会导致下次调度找不到 job;清理 job 应独立运维脚本操作
网络抖动或远端库宕机时,存储过程必须能断点续传
Oracle 的 DBLINK 查询一旦遇到网络中断,整个事务回滚,但已更新的 SYNC_LOG 时间点不会自动回退,下次运行就会漏数据。必须把“记录本次最大时间点”和“执行同步”包在同一个事务里,并用 EXCEPTION 捕获 ORA-03113(通信中断)、ORA-02068(远端致命错误)等典型异常。
- 示例关键逻辑:
BEGIN SELECT NVL(MAX(LAST_SYNC_TIME), DATE '1970-01-01') INTO v_last_time FROM SYNC_LOG; INSERT INTO SYNC_LOG (LAST_SYNC_TIME) VALUES (SYSDATE); COMMIT; FOR r IN (SELECT * FROM SOURCE@MY_LINK WHERE MODIFIED > v_last_time) LOOP MERGE INTO TARGET t USING (SELECT r.ID, r.NAME FROM DUAL) s ON (t.ID = s.ID) WHEN MATCHED THEN UPDATE SET t.NAME = s.NAME WHEN NOT MATCHED THEN INSERT VALUES (s.ID, s.NAME); END LOOP; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END; -
SYNC_LOG表必须建在本地库,且字段含STATUS VARCHAR2(10)和ERROR_MSG VARCHAR2(4000),便于事后排查 - 别忽略远端表统计信息过期问题:如果
SOURCE@MY_LINK执行很慢,先在远端执行EXEC DBMS_STATS.GATHER_TABLE_STATS('SCOTT','SOURCE')
最易被忽略的是 DBLINK 的连接超时参数——默认无超时,一次卡死就挂住整个 job 队列。必须在 USING 字符串里显式加 (CONNECT_TIMEOUT=10)(RECV_TIMEOUT=30),否则网络层故障会无限等待。











