データベースリンク(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 ネットワーク設定完全ガイドも参照してください。