【Oracle】データベースリンク(DATABASE LINK)完全ガイド|CREATE DATABASE LINK・リモート DB 参照・分散クエリ・管理方法まで解説

データベースリンク(DATABASE LINK)は、Oracle データベースから別の Oracle データベース(またはリモートサービス)に接続するためのオブジェクトです。テーブル名@リンク名 の形式でリモートのテーブルやビューをあたかも自分のスキーマにあるかのように参照できます。

本番 DB のデータを開発 DB で参照したり、複数の Oracle インスタンスをまたいだデータ統合クエリを実行したりと、分散環境での Oracle 運用でよく使われます。ただし、分散トランザクションや接続のオーバーヘッドについて理解しておく必要があります。

この記事でわかること

  • CREATE DATABASE LINK の構文と認証方式(FIXED USER / CONNECTED USER)
  • @リンク名 でリモートテーブル・ビューを参照・更新する方法
  • パブリックリンクとプライベートリンクの違い
  • 分散クエリと分散トランザクション(2フェーズコミット)の注意点
  • USER_DB_LINKS で接続先情報を確認する方法
  • ORA-02085・ORA-02019 などのエラーの対処法
スポンサーリンク

DATABASE LINK の作成

CREATE DATABASE LINK の基本構文
-- プライベートリンク(作成したユーザーのみが使える)
-- FIXED USER(固定ユーザー): リンクを使うすべてのユーザーが指定したユーザーとして接続する
CREATE DATABASE LINK prod_link
CONNECT TO hr IDENTIFIED BY hr_password
USING 'prod_db';           -- tnsnames.ora のエントリ名 または Easy Connect 文字列

-- Easy Connect 形式で直接指定する(tnsnames.ora なしで使える)
CREATE DATABASE LINK prod_link_ezc
CONNECT TO hr IDENTIFIED BY hr_password
USING '//prod-server.example.com:1521/orcl';

-- CONNECTED USER(接続ユーザー認証): 現在のセッションのユーザー名で接続する
-- ※ 接続先 DB に同名ユーザーが存在する必要がある
CREATE DATABASE LINK prod_link_cu
USING 'prod_db';           -- CONNECT TO 句なし → CONNECTED USER 認証

-- パブリックリンク(すべてのユーザーが使える・DBA 権限が必要)
CREATE PUBLIC DATABASE LINK pub_prod_link
CONNECT TO app_readonly IDENTIFIED BY app_password
USING '//prod-server.example.com:1521/orcl';

-- 必要な権限
-- プライベートリンク: CREATE DATABASE LINK システム権限
-- パブリックリンク: CREATE PUBLIC DATABASE LINK システム権限(DBA が付与)

DATABASE LINK を使ったリモートデータの参照

@リンク名 でリモートオブジェクトを参照する
-- リモートテーブルの参照(SELECT)
SELECT employee_id, last_name, salary
FROM employees@prod_link
WHERE department_id = 60;

-- リモートビューの参照
SELECT * FROM emp_summary_v@prod_link;

-- ローカルテーブルとリモートテーブルの結合(分散クエリ)
SELECT l.order_id, l.customer_id, r.product_name
FROM orders l
JOIN products@prod_link r ON l.product_id = r.product_id;

-- リモートテーブルへの INSERT・UPDATE・DELETE(分散トランザクション)
INSERT INTO orders@prod_link (order_id, customer_id, order_date)
VALUES (order_id_seq.NEXTVAL, 100, SYSDATE);

UPDATE employees@prod_link SET salary = salary * 1.1 WHERE department_id = 60;

DELETE FROM temp_log@prod_link WHERE created_at < SYSDATE - 30;

-- 分散トランザクションは 2フェーズコミット(2PC)が必要
-- ローカルとリモートの両方が COMMIT されるか、両方が ROLLBACK される
COMMIT;   -- 両方のサイトで一貫してコミットされる

-- リモートプロシージャの実行
EXEC update_orders@prod_link(order_id => 1001, status => 'SHIPPED');

-- リモートシーケンスの参照(12c 以降)
SELECT order_id_seq@prod_link.NEXTVAL FROM DUAL;
シノニムでリンク名を隠蔽する
-- @prod_link を毎回書く代わりにシノニムを作成する
CREATE SYNONYM prod_employees FOR employees@prod_link;
CREATE SYNONYM prod_orders FOR orders@prod_link;

-- シノニム経由でアクセス(@prod_link が不要になる)
SELECT * FROM prod_employees WHERE department_id = 60;

-- PUBLIC シノニム(全ユーザーが使える)
CREATE PUBLIC SYNONYM prod_products FOR products@prod_link;

-- アプリケーション側からはリンクの存在を隠蔽できる
-- → 将来ローカルにテーブルが移動してもシノニムを変更するだけでよい

分散トランザクションと 2 フェーズコミット

データベースリンクを使った UPDATE / INSERT / DELETE は分散トランザクションになります。Oracle は 2フェーズコミット(2PC)を使ってローカルとリモートの両方で確実にコミット・ロールバックを保証します。

分散トランザクションの注意点

  • ネットワーク障害が発生した場合、IN-DOUBT トランザクション(コミット待ち状態)が残ることがある
  • IN-DOUBT トランザクションは DBA_2PC_PENDING で確認し、必要に応じて手動でコミット / ロールバックする
  • 分散トランザクションは通常の単一 DB トランザクションよりオーバーヘッドが大きい
  • 可能であれば読み取り専用(SELECT のみ)のリンクにとどめ、書き込みはアプリケーション設計で分離するのが推奨
IN-DOUBT トランザクションの確認と解決
-- IN-DOUBT トランザクションを確認する
SELECT local_tran_id, global_tran_id, state, mixed, host, commit#
FROM DBA_2PC_PENDING;
-- STATE: prepared(コミット待ち)/ forced commit / forced rollback

-- IN-DOUBT トランザクションを手動で強制コミット / ロールバックする(DBA 権限が必要)
EXECUTE DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY('local_tran_id_here');
-- または
COMMIT FORCE 'local_tran_id_here';
ROLLBACK FORCE 'local_tran_id_here';
-- ※ IN-DOUBT トランザクションの解決は相手 DB の状態を確認してから行う

DATABASE LINK の確認と削除

DATABASE LINK の確認・削除コマンド
-- 自スキーマのリンクを確認する
SELECT db_link, username, host
FROM USER_DB_LINKS
ORDER BY db_link;
-- db_link: リンク名(大文字)
-- username: 接続ユーザー名(FIXED USER の場合)
-- host: tnsnames.ora の接続文字列または Easy Connect 文字列

-- すべてのリンク(DBA 権限が必要)
SELECT owner, db_link, username, host
FROM ALL_DB_LINKS
ORDER BY owner, db_link;

-- パブリックリンクを含むすべてのリンク
SELECT owner, db_link, username, host
FROM DBA_DB_LINKS
WHERE owner IN ('PUBLIC', 'HR', 'APP_USER')
ORDER BY owner, db_link;

-- 接続テスト(リンク先の DUAL テーブルを参照する)
SELECT 1 FROM DUAL@prod_link;

-- DATABASE LINK を削除する
DROP DATABASE LINK prod_link;
DROP PUBLIC DATABASE LINK pub_prod_link;   -- PUBLIC リンクは DBA 権限が必要

-- 現在のセッションでオープンされているリンクを確認する(V$DBLINK)
SELECT db_link, logged_on, open_cursors, in_transaction
FROM V$DBLINK;

よくあるエラーと対処法

エラー 原因 対処法
ORA-02085: database link connects to リンク先 DB の GLOBAL_NAME がリンク名と一致しない DB_DOMAIN パラメータを確認する。GLOBAL_NAMES=FALSE に設定するか、リンク名を GLOBAL_NAME に合わせる
ORA-02019: connection description for remote database not found tnsnames.ora にリンクで指定した接続文字列が見つからない tnsnames.ora の USING 句の接続文字列を確認する。tnsping で名前解決を確認する
ORA-01017: invalid username/password FIXED USER で指定したユーザー名・パスワードが間違っている リンク先 DB にユーザーが存在するか・パスワードの大文字小文字を確認する
ORA-12541: TNS:no listener リンク先 DB のリスナーが起動していないかポートが違う リンク先サーバーで lsnrctl status を確認する
ORA-02055: distributed update operation failed 分散 UPDATE 中に障害が発生した DBA_2PC_PENDING で IN-DOUBT トランザクションを確認して手動解決する
ORA-02085 の対処(GLOBAL_NAMES の確認)
-- GLOBAL_NAMES パラメータを確認する
SHOW PARAMETER global_names;
-- TRUE(デフォルト): リンク名がリンク先の GLOBAL_NAME と一致する必要がある
-- FALSE: リンク名とリンク先の GLOBAL_NAME が一致しなくてもよい

-- リンク先 DB の GLOBAL_NAME を確認する(リンク先で実行)
SELECT * FROM GLOBAL_NAME;  -- orcl.example.com などのドメイン付き名前

-- 対処法①: リンク名を GLOBAL_NAME に合わせる
CREATE DATABASE LINK orcl.example.com   -- リンク名をリンク先の GLOBAL_NAME に合わせる
CONNECT TO hr IDENTIFIED BY hr_password
USING 'prod_db';

-- 対処法②: GLOBAL_NAMES を FALSE に設定する(開発環境では楽だが本番は要注意)
ALTER SYSTEM SET GLOBAL_NAMES = FALSE;
ALTER SESSION SET GLOBAL_NAMES = FALSE;  -- セッションレベルでも設定できる

まとめ

  • CREATE DATABASE LINK:CONNECT TO ユーザー IDENTIFIED BY パスワード USING 接続文字列 で定義する。CONNECTED USER 認証(CONNECT TO 句なし)は接続先に同名ユーザーが必要
  • @リンク名:テーブル名・ビュー名・シーケンス名・プロシージャ名の後に付けてリモートオブジェクトを参照する。シノニムでリンク名を隠蔽するとアプリケーションを柔軟にできる
  • パブリック vs プライベート:PUBLIC DATABASE LINK は全ユーザーが使える。DBA 権限が必要。セキュリティを考慮して読み取り専用ユーザーで接続するパブリックリンクを使うことが多い
  • 分散トランザクション:リンク経由の DML は 2フェーズコミット(2PC)になる。ネットワーク障害時は IN-DOUBT トランザクションが発生することがある。DBA_2PC_PENDING で確認・手動解決する
  • ORA-02085:GLOBAL_NAMES=TRUE のときリンク名とリンク先の GLOBAL_NAME が一致しないと発生する。リンク名を GLOBAL_NAME に合わせるか GLOBAL_NAMES=FALSE で対処する

データベースリンクを使った DB 間データ転送は Data Pump の NETWORK_LINK でも活用できます。詳細は Oracle Data Pump(expdp/impdp)完全ガイドを参照してください。ネットワーク設定(tnsnames.ora)については Oracle ネットワーク設定完全ガイドも参照してください。