OracleとMySQLでDB LINKをしてみる

MySQLからOracleへmysqldump以外の方法でデータ移行ができないか模索するためDB LINKを作ってみました。

目指すのはOracleからMySQLのデータを取り出すこと。

手順は[kenken0807](http://kenken0807.hatenablog.com/entry/2015/07/27/144953)さんの検証とほほ同じ。
ただUTF8環境の場合は独自のハマりポイントもあったためそちらについても記載しています。
調べてみるまではネット上にナレッジがいっぱい落ちていると思ってたのに、全然なかったしね。
少しでも参考になれば幸いです。

# 環境
|RDBMS |DB version|OS          |OS version|IP address    |DBの文字コード|
|:----:|:--------:|:----------:|:--------:|:------------:|:--------:|
|Oracle|18.3.0.0.0|Oracle Linux|7.6       |192.168.56.101|utf8      |
|MySQL |5.7.25    |Oracle Linux|7.6       |192.168.56.111|utf8      |

# MySQL側作業
先にDB LINK用ユーザの作成と権限の付与を行っておきます。
## 1. DB LINK用ユーザの作成

```sql
create user 'from_oracle'@'192.168.56.101' identified by 'from_oracle';
```

## 2. DB LINK用ユーザに接続先データベースへの権限付与

```sql
grant all privileges on db_migration.* to 'from_oracle'@'192.168.56.101';
```


# Oracle側作業
MySQLとはODBCを経由してDB LINKを行います。
## 3. unixODBC (ODBC Driver Manager)のインストール

```bash
yum install unixODBC
```

unixODBCの設定を確認

```bash
odbcinst -j
```

```:例
[root@local01 ~]# odbcinst -j
unixODBC 2.3.1
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /root/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
[root@local01 ~]#
```
SYSTEM DATA SOURCESのパスを確認しておく。

## 4. mysql-connector-odbc (MySQL用ODBCドライバ) のインストール
Oracle Linuxのリポジトリ [ol7_latest] から入手するとドライバのバージョンが結構古い(5.2.5-8)ので、公式から最新版をインストール。

```bash
yum install https://dev.mysql.com/get/Downloads/Connector-ODBC/8.0/mysql-connector-odbc-8.0.15-1.el7.x86_64.rpm
```

最新版はこちらからも入手可能。
http://dev.mysql.com/downloads/connector/odbc/#downloads

## 5. unixODBCのMySQL用ODBC Driverの設定確認

unixODBCおよびmysql-connector-odbcのインストールを行うと、自動的に作成＆エントリーの追加がされているはず。

```bash
cat /etc/odbcinst.ini
```

```ini:/etc/odbcinst.ini
[PostgreSQL]
Description=ODBC for PostgreSQL
Driver=/usr/lib/psqlodbcw.so
Setup=/usr/lib/libodbcpsqlS.so
Driver64=/usr/lib64/psqlodbcw.so
Setup64=/usr/lib64/libodbcpsqlS.so
FileUsage=1

[MySQL]
Description=ODBC for MySQL
Driver=/usr/lib/libmyodbc5.so
Setup=/usr/lib/libodbcmyS.so
Driver64=/usr/lib64/libmyodbc5.so
Setup64=/usr/lib64/libodbcmyS.so
FileUsage=1

[MySQL ODBC 8.0 Unicode Driver] # ★今回使用するのはこのODBCドライバ
Driver=/usr/lib64/libmyodbc8w.so
UsageCount=1

[MySQL ODBC 8.0 ANSI Driver]
Driver=/usr/lib64/libmyodbc8a.so
UsageCount=1
```

## 6. MySQLデータソースの設定

項番3で確認したSYSTEM DATA SOURCESのファイルを編集する。

```bash
vi /etc/odbc.ini
```

<font color="RED">Driverには項番5で確認したドライバの名前を入れる。
データベース名の大文字小文字にも要注意。</font>

```ini:/etc/odbc.ini
# 任意のデータソース名
[MYDB]
# /etc/odbcinst.iniのドライバ名
# 8.0系かつUnicodeのDriverを利用
Driver     = MySQL ODBC 8.0 Unicode Driver
# MySQLのホスト名またはIPアドレス
SERVER     = 192.168.56.111
# MySQLのポート番号
PORT       = 3306
# MySQLの接続するデータベース名
DATABASE   = db_migration
# MySQLへ接続するユーザ名
USER       = from_oracle
# MySQLへ接続するユーザのパスワード
PASSWORD   = from_oracle
# キャラクタセット
CHARSET    = utf8
```

## 7. ODBC接続チェック

isqlコマンドを使用してODBC接続してみる。

```bash
isql MYDB -v
```

```sql:例
[root@local01 ~]# isql MYDB -v
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL> select user();
+----------------------------------------------------------------------------------------------+
| user()                                                                                       |
+----------------------------------------------------------------------------------------------+
| from_oracle@192.168.56.101                                                                   |
+----------------------------------------------------------------------------------------------+
SQLRowCount returns 1
1 rows fetched
SQL> show databases;
+-----------------------------------------------------------------+
| Database                                                        |
+-----------------------------------------------------------------+
| information_schema                                              |
| db_migration                                                    |
+-----------------------------------------------------------------+
SQLRowCount returns 2
2 rows fetched
SQL> quit
[root@local01 ~]#
```

## 8. ODBCドライバマネージャーライブラリのパスを確認

項番11のゲートウェイ初期化パラメータファイル設定時に使用するため事前に確認する。

```bash
ldd `which isql`
```

```bash:例
[root@local01 ~]# ldd `which isql`
        linux-vdso.so.1 =>  (0x00007fff38bb9000)
        libodbc.so.2 => /lib64/libodbc.so.2 (0x00007ffa4a18e000) # ★ここがODBCドライバマネージャーライブラリのパス
        libltdl.so.7 => /lib64/libltdl.so.7 (0x00007ffa49f84000)
        libreadline.so.6 => /lib64/libreadline.so.6 (0x00007ffa49d3d000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007ffa49b39000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ffa4991d000)
        libc.so.6 => /lib64/libc.so.6 (0x00007ffa4954f000)
        libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007ffa49325000)
        /lib64/ld-linux-x86-64.so.2 (0x0000558bd097b000)
[root@local01 ~]#
```

## 9. listener.oraの編集

既存のリスナーに影響を与えぬよう、別途1522ポートでゲートウェイ用リスナーを作成してます。

```bash
vi ${ORACLE_HOME}/network/admin/listener.ora
```

下記エントリーを追記。
<font color="RED">SID_NAMEには/etc/odbc.iniで定義したデータソース名を。
dg4odbcは小文字で。
</font>

```:${ORACLE_HOME}/network/admin/listener.ora
LISTENER2 =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.56.101)(PORT = 1522))
    )
  )

SID_LIST_LISTENER2 =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = MYDB)
      (ORACLE_HOME = /opt/oracle/product/18c/dbhome_1)
      (PROGRAM = dg4odbc)
    )
  )
```

## 10. tnsnames.oraの編集

```bash
vi ${ORACLE_HOME}/network/admin/tnsnames.ora
```

ゲートウェイ用の下記エントリーを追加する。
<font color="RED">SIDにはlistener.oraで指定したSID_NAMEを。</font>

```:${ORACLE_HOME}/network/admin/tnsnames.ora
MYDB =
  (DESCRIPTION=
      (ADDRESS=
         (PROTOCOL=TCP)
         (HOST=192.168.56.101)
         (PORT=1522)
      )
      (CONNECT_DATA=
         (SID = MYDB)
      )
      (HS=OK)
  )
```

## 11. ゲートウェイ初期化パラメータファイルを作成
ゲートウェイ初期化パラメータファイルは$ORACLE_HOME/hs/admin配下にinit\<SID>.oraと言う名前で作成する。
initdg4odbc.oraというサンプルファイルがあるので、それをコピーして作ってもいい。
<font color="RED">\<SID>は/etc/odbc.iniのデータソース名(DSN)とイコール。
データソース名を大文字にしている場合は初期化パラメータファイルも大文字で作る必要がある。</font>

```bash
vi ${ORACLE_HOME}/hs/admin/initMYDB.ora
```

以下の内容を記載。

```bash:${ORACLE_HOME}/hs/admin/initMYDB.ora
# /etc/odbc.iniのデータソース名
HS_FDS_CONNECT_INFO=MYDB
# Oracleの文字コードがUTF8の場合以下の通りに設定する
HS_NLS_NCHAR=UCS2
HS_LANGUAGE=JAPANESE_JAPAN.AL32UTF8
# 7.で確認したODBCドライバマネージャーライブラリのパス
HS_FDS_SHAREABLE_NAME=/lib64/libodbc.so.2
# ODBCINI変数に参照するiniファイルのパスを指定
set ODBCINI=/etc/odbc.ini
```

<font color="RED">特にHS_NLS_NCHAR, HS_LANGUAGEの設定がうまくいっていないとDB LINK設定後、MySQLのデータを参照しようとしても以下のエラーが出てハマる。</font>

```
ERROR at line 1: 
ORA-28500: connection from ORACLE to a non-Oracle system returned this message: 
[
```

うまくいかないときは、```HS_FDS_TRACE_LEVEL=debug```を設定すること。
$ORACLE_HOME/hs/logにトレースファイルが出力されるようになるのでエラー原因が掴めるかも…。

## 12. リスナー起動
作成したリスナーを起動する。

```bash
lsnrctl start listener2
```

## 13. DB LINK作成

今回はPDB上でDB LINKを作成してますが、特にPDBだからうまくいかないと言ったことはありませんでした。

```sql
CREATE PUBLIC DATABASE LINK DBLINK_MYSQL CONNECT TO "from_oracle" IDENTIFIED BY "from_oracle" USING 'MYDB';
```


## 14. MySQLのデータ参照

```sql:例
SQL> select count(*) from "emp"@DBLINK_MYSQL;

  COUNT(*)
----------
        10

SQL> select "id", "desc" from "emp"@DBLINK_MYSQL where rownum <= 1;

     id desc
------- ----------------------------------------
      1 説明あれこれ

```

## 終わりに
実際にはかなりの試行錯誤がありました。
DB LINKを使うことでmysqldumpではできない、段階的なデータ移行ができたらいいなと思ってます。

## 参考にしたサイト
OracleからMySQLへODBC経由のデータベースリンクで接続したメモ
http://kenken0807.hatenablog.com/entry/2015/07/27/144953
Oracle HS fails to operate on mysql-connector-odbc-5.3.4-1.el7
https://bugs.mysql.com/bug.php?id=78242
https://bugs.mysql.com/file.php?id=23311&bug_id=78242
Error Ora-28500 and Sqlstate I or Displays a Square Bracket - [ - Issuing Selects From a Unicode Oracle RDBMS With Dg4odbc To Non-Oracle Databases Using the UnixODBC Driver Manager (ドキュメントID 756186.1)
https://support.oracle.com/epmos/faces/DocumentDisplay?id=756186.1
(参照にはOTNアカウントが必要)