Aekor

Aekor
专注于用户阅读体验的响应式博客主题
  1. 首页
  2. Oracle 数据库
  3. 正文

Oracle DB Link:原理、类型与完整创建过程

2026-03-13 17974点热度 0人点赞 0条评论

跨库查一张表,DB Link 往往最省事:不搬数据、不做同步,一条 SQL 读到远端。但它把网络、认证、事务、权限绑在一条链接上,任何一环没配对,报错都指向别处。下面先讲清它做了什么、有哪些类型,再给一套可照抄的创建过程。

一、DB Link 是什么

数据库链接(Database Link)是 Oracle 在一个数据库实例中访问另一个实例对象的机制。通过它,可以对远端表做查询、插入、更新、删除,语法上只是在对象名后加 @链接名。

它不复制数据,也不缓存结果。每次引用都是真实的远程调用:本地解析、按链接定义连远端、远端认证执行、结果回传。后面所有性能和故障问题都由这一点解释。

1.1 一次跨库查询的完整路径

以 select count(*) from as_user_info@asuser_as_user_info 为例:

  1. 本地会话解析 SQL,识别出对象带 @,取出链接定义里的连接描述。
  2. 按连接描述里的 HOST、PORT、SERVICE_NAME 发起 TCP 连接。连接描述可以写在链接定义里,也可以只写一个 TNS 别名,由 tnsnames.ora 或 LDAP 解析。
  3. 远端监听接手,把连接交给对应服务;远端用链接里指定的账号密码认证,建立会话。
  4. SQL 被送到远端执行。绝大多数情况下,过滤、聚合、排序都在远端完成,只把结果集传回本地。
  5. 如果语句里同时有本地表和远端表,本地要做 join,这时才会有大量数据在网络上往返。

第 4 步是关键:count(*) 只回传一行,代价很小;select * 加本地 join 则可能把远端整张表拉过来。跨库 SQL 慢,先看执行计划在哪一端跑。

1.2 链接名、schema 与对象解析

select * from as_user_info@link 里,as_user_info 按远端账号的默认 schema 解析。链接用 CONNECT TO asuser 建立,远端会话的 schema 就是 ASUSER,不写 schema 也能查到。

改用 CONNECT TO CURRENT_USER 后,远端会话的 schema 变成当前登录用户,同一条 SQL 就报 ORA-00942。这时必须写全 ASUSER.AS_USER_INFO@link,或在远端建同义词。

1.3 优化器看到的是什么

本地 CBO 对远端表的统计信息通常一无所知,只能按默认选择率估算。两张表各自不大,join 却选了嵌套循环、驱动顺序反了,这类问题在 DB Link 场景很常见。

可控的做法有三种:把过滤条件写进远端视图;用 /*+ driving_site(别名) */ 把整条语句推到远端;或放弃实时 join 改成定时抽取。第三种最土,但数据量大时往往最稳。

1.4 事务与两阶段提交

本地对远端表做 DML 后提交,Oracle 会用两阶段提交协调本地和远端。正常时感觉不到差别,网络中断时就可能出现 in-doubt 事务:一端已提交、另一端结果未知,需要 DBA 介入。

所以 DB Link 适合读多写少。业务要求强一致的跨库写时,先评估网络质量和故障恢复流程,不要默认它像单库事务一样工作。

二、三种类型与三种身份

2.1 私有、公有、全局

类型创建语法可见范围
私有CREATE DATABASE LINK只有创建者能使用
公有CREATE PUBLIC DATABASE LINK库内所有用户都能使用
全局CREATE DATABASE LINK ... CONNECT TO CURRENT_USER由目录服务(如 Oracle Names)定义,网络内多个数据库可用

私有链接建在用户 schema 下,跟着用户走,权限边界清晰,生产优先用。公有链接省事,但任何能连库的人都能借用其中的远端账号,等于把远端权限放大到全库。

三种类型的实际差别不在语法,而在“谁看得见”。同一句 select * from t@link,用私有链接必须由 owner 执行;换成公有链接,任何用户都能执行,用的还是链接里那个远端账号的身份。这就是公有链接在生产环境要严格管控的原因。

全局链接依赖目录服务,普通单机环境基本用不到。它的经典定义在 Oracle Names 服务器上,而 Oracle Names 早已不是主流,实际项目更常见的是 LDAP 或干脆不用。

2.2 固定用户、连接用户、当前用户

链接定义里 CONNECT TO 的写法决定了用谁的身份连远端:

CONNECT TO asuser IDENTIFIED BY "密码" 是固定用户。密码存在数据字典里,DBA_DB_LINKS 不显示,但有高权限的账号仍可能取到。行为确定,不依赖本地登录用户。

固定用户最常见,代价是密码轮换麻烦:远端改密后,所有引用该链接的库都要重建链接,否则下次连接报 ORA-01017。改密流程要把 DB Link 列进影响面清单。

CONNECT TO CURRENT_USER 用当前会话的用户名去远端认证,要求两端有同名同密码的账号。它避免在链接里存密码,但账号必须两端同步。

完全不写 CONNECT TO 时默认用本地登录用户去远端认证,同样要求两端账号一致。这种方式最易被忽略:创建时不报错,查询时才报 ORA-01017。

2.3 权限

建链接和用链接是两套权限,很多人只记住了前者。

创建链接需要 CREATE DATABASE LINK;建公有链接需要 CREATE PUBLIC DATABASE LINK,通常只有 DBA 有。这只是“建”的权限。

“用”的权限在远端校验:远端账号必须对目标表有 SELECT/INSERT/UPDATE/DELETE。本地用户即使能写 select * from tab@link,远端没授权照样报 ORA-00942 或 ORA-01031。

还有一层容易漏:私有链接只有 owner 能用;公有链接所有人可见,但远端对象权限仍按链接里保存的账号算。

审计时可以用这两条查清现状:

select owner, db_link, username, host, created
from dba_db_links
order by owner, db_link;

select * from v$dblink;

v$dblink 是当前实例已打开的链接会话,能看到 LOGON_TIME 和 IN_TRANSACTION;排查“谁在用链接”时比 DBA_DB_LINKS 有用。

三、创建前的检查

3.1 网络通不通

先在源端服务器确认能连到目标端监听端口,不要只在客户端测:

tnsping assdb
nc -vz 192.168.61.131 1526

tnsping 走 TNS 解析加监听握手,能通说明别名和网络都没问题;nc 只测 TCP,可区分网络不通和 TNS 配错。

只测通还不够。跨机房或跨云的网络,TCP 能连上但 RTT 高、丢包重传多,DB Link 上每条 SQL 都要付这个延迟。先量:

ping -c 20 192.168.61.131

平均 RTT 超过 10ms 时,不适合在链接上做逐行处理(如 insert ... select 一百万行),改成远端聚合或批量抽取。

3.2 服务名对不对

到目标端确认监听注册的服务名,不要凭记忆写:

lsnrctl status
select name, pdb from v$services order by name;

SERVICE_NAME 写错时报 ORA-12514(监听不认识这个服务),而不是“连不上”。RAC 下还要确认连的是 SCAN 还是某个 VIP。

PDB 环境要注意:v$services 在 CDB 根能看到所有 PDB 的服务,只有 con_id 对应的才是要连的。用 show con_name 确认当前容器,再对照服务列表:

show con_name
select name, con_id, pdb from v$services order by con_id;

PDB 刚创建、监听里还没有它的服务时,先在 PDB 里执行 alter system register;,再看 lsnrctl status。

3.3 两端账号与权限

目标用户要存在、有配额、默认表空间正确。PDB 环境下尤其注意:用户建在哪个容器,链接就必须连到那个容器的服务名,连到 CDB 根看不到 PDB 用户的表。

源端用户要有 CREATE DATABASE LINK;建公有链接还要 CREATE PUBLIC DATABASE LINK,并且想清楚谁会用它。

四、完整实操

环境:源端 192.168.61.132,CDB 为 GACDB,PDB 为 GADB,用户 GAUSER;目标端 192.168.61.131,CDB 为 ASSCDB,PDB 为 ASSDB,用户 ASUSER。需求是源端 GAUSER 通过 DB Link 访问目标端 ASUSER.AS_USER_INFO。

4.1 目标端:建表空间和用户

sqlplus / as sysdba
alter session set container=ASSDB;

CREATE tablespace ASUSER_TBS datafile '/oracle/app/db/oradata/ASSCDB/assdb/asuser_tbs.dbf' SIZE 200M autoextend ON;
CREATE USER ASUSER identified BY "AS#0312#dba" DEFAULT tablespace ASUSER_TBS;

GRANT CONNECT,resource,unlimited tablespace TO ASUSER;

sqlpuls 是笔误,实际命令是 sqlplus。alter session set container 只在连到 CDB 根容器时有效,直接用 PDB 服务名登录不需要。密码加双引号是为了保留大小写并明确边界;不加引号时 Oracle 对大小写的处理和加引号不同,建链接时密码对不上就报 ORA-01017。

4.2 目标端:建表、索引、造数据

CREATE TABLE ASUSER.AS_USER_INFO (
  user_id number(10) PRIMARY KEY,
  user_name varchar2(50) NOT NULL,
  email varchar2(100),
  create_time date DEFAULT sysdate,
  status number(1) DEFAULT 1
);

-- 为 user_name 创建索引(提升查询效率)
CREATE INDEX idx_as_user_info_name ON ASUSER.AS_USER_INFO(user_name);

DECLARE
    v_max_rows NUMBER := 100000;
BEGIN
    FOR i IN 1..v_max_rows LOOP
        INSERT INTO asuser.as_user_info (user_id, user_name, email)
        VALUES (
            i,
            'user_' || LPAD(i, 6, '0'),
            'user_' || LPAD(i, 6, '0') || '@test.com'
        );
        -- 每 1000 条提交一次,避免 UNDO 日志过大
        IF MOD(i, 1000) = 0 THEN
            COMMIT;
        END IF;
    END LOOP;
    COMMIT;
END;
/

分 100 次提交是为了控制 UNDO 和日志增长。数据量再大时这段 PL/SQL 会很慢,改用 insert /*+ append */ ... select ... from dual connect by level <= 100000 或外部表。

4.3 源端:建用户并授权

sqlplus / as sysdba
alter session set container=GADB;

CREATE tablespace GAUSER_TBS datafile '/oracle/app/db/oradata/GACDB/gadb/gauser01.dbf' SIZE 200M autoextend ON;
CREATE USER GAUSER identified BY "your_password" DEFAULT tablespace GAUSER_TBS;

GRANT CONNECT,resource,unlimited tablespace TO gauser;
GRANT CREATE database link TO gauser;

这里只授了 CREATE DATABASE LINK,建出来的是 GAUSER 的私有链接。确实需要公有链接时,把权限换成 CREATE PUBLIC DATABASE LINK,并确认远端账号的权限范围可接受。

4.4 源端:创建 DB Link

创建之前先确认当前用户在正确的容器里、有建链接的权限。私有链接属于创建它的用户,可见范围跟着容器走:在 PDB 里建,只在那个 PDB 可见;在根容器用公共用户建,各 PDB 才可能用到。拿不准时,建完到目标容器里查一次 DBA_DB_LINKS 验证。

show con_name
select * from session_privs where privilege like '%DATABASE LINK%';

第二条查不到任何行,说明当前用户没有建链接的权限,先授权再继续。

sqlplus 'gauser/"your_password"@GADB'
CREATE database link asuser_as_user_info
CONNECT TO asuser identified BY "AS#0312#dba"
USING '(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.61.131)(PORT = 1526)))(CONNECT_DATA = (SERVICE_NAME = assdb)))';

这一行若写成 sqlplus "gauser/"your_password"@GADB',引号不配对,直接复制会报错。上面改成单引号包整串,让 SQL*Plus 解析里面的双引号密码。

链接名建议写成“远端用户 + 对象或用途”,别用 link1、test,半年后没人知道它连的是哪。

USING 直接写完整连接描述,不依赖本地 tnsnames.ora。换机器不用改 TNS,但地址变了要逐个改链接;链接多、地址常变时改用 TNS 别名:

CREATE database link asuser_as_user_info
CONNECT TO asuser identified BY "AS#0312#dba"
USING 'ASSDB';

两种写法各有代价。硬编码描述在目标端换 IP 时要重建所有链接;用别名则依赖源端 tnsnames.ora 的维护,别名写错时报 ORA-12154,看起来像网络问题。跨机房切换、VIP 漂移的场景,别名更容易集中改一处。

4.5 源端:验证

SELECT * FROM dual@asuser_as_user_info;
SELECT count(*) FROM AS_USER_INFO@asuser_as_user_info;

第一条验证链接能连上,第二条验证远端对象权限。两条都过才算完整;只有第一条过、第二条报 ORA-00942,问题在远端授权。

想确认实际连到哪个实例,可以查:

select instance_name, host_name from v$instance@asuser_as_user_info;

4.6 源端:建同义词

不想每条 SQL 都写 @链接名,可以建同义词:

-- 如果 gauser 没有创建同义词权限,则使用 sys 账号赋予
grant create synonym to gauser;

create synonym ASUSER_AS_USER_INFO FOR AS_USER_INFO@asuser_as_user_info;

-- 可以通过同义词进行访问了
select count(*) from ASUSER_AS_USER_INFO;

同义词只是语法糖,不改变权限模型:查询仍用链接里保存的远端账号执行。它也容易让人误以为是本地表,命名最好带 _LINK 或远端系统前缀。

五、跨库 SQL 怎么写才不慢

同一个需求,写法不同,性能可能差两个数量级。下面三条规则按收益从高到低排。

过滤条件尽量留在远端。 在本地写 where 不等于在远端过滤。优化器可能把整表拉回本地再筛,尤其是远端统计信息缺失时。把条件固化到远端视图里,本地只做 select * from 视图@link,执行计划就稳定了:

-- 远端执行
create or replace view asuser.v_user_info_active as
select user_id, user_name, email, create_time
from asuser.as_user_info
where status = 1;

需要 join 时考虑 driving_site。 提示让整条语句在远端执行,只回传最终结果:

select /*+ driving_site(a) */ a.user_id, b.dept_name
from asuser.as_user_info@asuser_as_user_info a,
     local_dept b
where a.user_id = b.user_id;

这个提示不是万能的。远端要能访问本地表才行,否则语句根本推不过去。真正的跨库 join 仍然会分两段执行。

批量操作改成分批。insert into local_tab select * from remote_tab@link 会在一个事务里锁住两端,回滚段和 UNDO 都吃不消。改成按主键范围分批,每批提交:

insert into local_tab
select * from remote_tab@link
where id between 1 and 10000;
commit;

每批大小按网络 RTT 和 UNDO 容量调,没有固定值。批太小,网络往返次数多;批太大,UNDO 撑不住。

六、日常维护

链接建好只是开始,还有三件事要定期做。

验证可用性。 不要等业务报错才发现链接断了。放一条轻量检查进监控,每分钟跑一次:

select 1 from dual@asuser_as_user_info;

这条 SQL 不碰业务表,只验证网络、监听、认证。失败时告警,比业务方打电话快。

查看链接会话。 远端实例上能看到链接过来的会话,定位慢 SQL 时从这里入手:

select s.sid, s.serial#, s.username, s.program, s.machine, s.status
from v$session s
where s.username = 'ASUSER'
  and s.program like '%ORACLE%';

program 带 ORACLE 且 machine 是源端主机名的,基本就是 DB Link 会话。

删除链接。 不再使用的链接要显式删掉,避免权限残留:

drop database link asuser_as_user_info;

公有链接用 drop public database link 链接名;。删除前确认没有同义词、视图、存储过程依赖它,否则编译会失败。

select owner, name, type
from dba_dependencies
where referenced_name = 'ASUSER_AS_USER_INFO';

七、常见报错怎么定位

ORA-02019: connection description for remote database not found。链接名拼错,或 USING 里的 TNS 别名解析不到。先 select * from dba_db_links where db_link like '%关键字%' 确认名字,再 tnsping 确认别名。

ORA-12154: TNS:could not resolve the connect identifier specified。USING 用了别名但 tnsnames.ora 里没有,或者客户端读的不是你以为的那个 tnsnames.ora(TNS_ADMIN 指错)。

ORA-12514: TNS:listener does not currently know of service requested。网络通了、监听也通了,但服务名不对,或 PDB 没注册到监听。到目标端 lsnrctl status 看服务列表。

ORA-01017: invalid username/password; logon denied。密码大小写、特殊字符没加引号、两端账号不一致(用 CURRENT_USER 时)。注意 11g 之后密码默认区分大小写。

ORA-00942: table or view does not exist。链接是通的,但远端账号对表没权限,或者表在另一个 PDB/schema 里。用 select * from tab@link 看远端账号能看到什么。

ORA-02063: preceding line from 链接名。这不是独立错误,只是告诉你“上面那条错来自哪个链接”,真正的错误码在它前一行。

八、生产环境的几条建议

链接密码存在数据字典中,DBA_DB_LINKS 不直接显示,但有 DBA 权限的账号仍可能获取。给链接单独建远端账号,只授 SELECT 和必要的 INSERT/UPDATE,并限制来源 IP。

跨库大查询尽量在远端完成过滤和聚合。可在远端建视图封装过滤条件,本地只查视图,比本地写复杂 join 更稳定。

分布式事务出问题时,DBA_2PC_PENDING 会有 in-doubt 记录,要有处理预案。定期清理 DBA_DB_LINKS 里不用的旧链接,既满足权限要求,也少一类排错干扰。

勘误与待核对

  • sqlpuls / as sysdba 为笔误,文中按 sqlplus 给出。
  • sqlplus "gauser/"your_password"@GADB' 引号不配对,文中改为单引号包裹,需在实际环境验证。
  • 全局链接“在 Oracle Names 服务器上定义”是传统说法,Oracle Names 已非主流,实际是否可用取决于是否部署了目录服务。
  • 示例中的 IP、端口 1526、服务名 assdb/gadb、密码均为实验环境值,不要直接用于生产。
  • 若目标端 PDB 未注册到监听,需先确认 local_listener 与 lsnrctl status 中的服务名。

原文首发于微信公众号「止水研习社」。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: DB Link Oracle 数据集成
最后更新:2026-10-17

Aekor

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

使用AI教程

  • API报错解决方案
  • API 基础知识
  • API Key 获取
  • 最新接入教程

分类

  • Blog
  • Oracle 数据库
  • TradingAgents-CN
  • 使用教程
  • 数据库

COPYRIGHT © 2026 Aekor. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang