ストレージエンジンの話
MariaDBは様々な方式でデータを持つことができ、そこにアクセスするための仕組みとして「ストレージエンジン」を搭載している。
例えば「MariaDB」と聞いて一般的にイメージする、RDBMSとしてデータを持つ形式は「InnoDB」ストレージエンジンが使われている。
他にもメモリ上にテーブルを保持する「Memory」や水平分散が可能な「Spider」など、様々なエンジンがある。
MySQLのストレージエンジンについて #MySQL - Qiita
その中でも「Federated」は、ネットワーク上にある他のMariaDB/MySQLに接続するためのエンジンであり、残念ながらOracleやSQL Server等の他DBMSには対応していないようだ。
CONNECTエンジンに舵を切る
どうしたものかとさらに検索してみたところ、「CONNECT」エンジンというものが求めているものらしい。
CONNECT STORAGE ENGINEを利用した異なるDBMSへの透過的接続 - Database JUNKY
CONNECTは汎用性の高いODBC経由で接続できるため、大抵のDBMSには対応しているとのこと。これだ。
(ちなみに、FEDERATEDはMySQL/MariaDB双方に存在するが、CONNECTはMariaDB独自の模様)
早速インストール手順に従って sudo yum install MariaDB-connect-engine を叩いたが見つからず。
公式のガイドに従ってパッケージリポジトリを追加しようとしたがAmazon Linux 2023が見つからず。
あれー、と思って sudo yum list --available mariadb* と叩いたら、
Available Packages
mariadb-connector-c-devel.x86_64 3.1.13-1.amzn2023.0.3 amazonlinux
mariadb-connector-c-test.x86_64 3.1.13-1.amzn2023.0.3 amazonlinux
mariadb105-connect-engine.x86_64 3:10.5.23-1.amzn2023.0.1 amazonlinux
mariadb105-devel.x86_64 3:10.5.23-1.amzn2023.0.1 amazonlinux
mariadb105-oqgraph-engine.x86_64 3:10.5.23-1.amzn2023.0.1 amazonlinux
mariadb105-pam.x86_64 3:10.5.23-1.amzn2023.0.1 amazonlinux
mariadb105-rocksdb-engine.x86_64 3:10.5.23-1.amzn2023.0.1 amazonlinux
mariadb105-sphinx-engine.x86_64 3:10.5.23-1.amzn2023.0.1 amazonlinux
mariadb105-test.x86_64 3:10.5.23-1.amzn2023.0.1 amazonlinux
名前が違っていたというオチ。無事インストール完了。
そのままMariaDBにCONNECTエンジンを登録する。
mariadb -u username -p
Enter password:
MariaDB [(none)]> INSTALL SONAME 'ha_connect.so';
Query OK, 0 rows affected (0.005 sec)
MariaDB [(none)]> show engines;
+--------------------+---------+-------------------------------------------------------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+-------------------------------------------------------------------------------------------------+--------------+------+------------+
| Aria | YES | Crash-safe tables with MyISAM heritage. Used for internal temporary tables and privilege tables | NO | NO | NO |
| MRG_MyISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| MyISAM | YES | Non-transactional engine with good performance and small data footprint | NO | NO | NO |
| CSV | YES | Stores tables as CSV files | NO | NO | NO |
| ARCHIVE | YES | gzip-compresses tables for a low storage footprint | NO | NO | NO |
| FEDERATED | YES | Allows one to access tables on other MariaDB servers, supports transactions and more | YES | NO | YES |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| InnoDB | DEFAULT | Supports transactions, row-level locking, foreign keys and encryption for tables | YES | YES | YES |
| SEQUENCE | YES | Generated tables filled with sequential values | YES | NO | YES |
| CONNECT | YES | Management of External Data (SQL/NOSQL/MED), including Rest query results | NO | NO | NO |
+--------------------+---------+-------------------------------------------------------------------------------------------------+--------------+------+------------+
↑一番下に「CONNECT」が追加された。ひとまずインストールは完了。
Oracleへの接続設定
Oracleへの接続方法を書いてくれているブログが見つかったので、ここを参考に進めていく。
MariaDB CONNECT Storage Engine access to Oracle 11GR2 - Serge Frezefond 's blog
Oracle Instant Client(ODBCドライバ)のインストール
OracleのODBCドライバであるOracle Instant Clientをダウンロードする。
上記リンクから「Instant Client for Linux x86-64」をクリックし、
- Basic Package (ZIP)
- SDK Package (ZIP)
- ODBC Package (ZIP)
をwgetしてunzipする。場所は適当に /opt/oracle/instantclient_23_4 とかに。
場所が決まったら .bash_profile に下記2行を追加しておく。
CLIENT_HOME=/opt/oracle/instantclient_23_4 ; export CLIENT_HOME
LD_LIBRARY_PATH=$CLIENT_HOME:$LD_LIBRARY_PATH ; export LD_LIBRARY_PATH
unixODBCのインストール
次に sudo yum install unixODBC unixODBC-devel を叩く。
インストール後、 odbcinst -j を叩くと各種情報が表示される。
unixODBC 2.3.9
DRIVERS............: /etc/odbcinst.ini ←この2ファイルを編集する
SYSTEM DATA SOURCES: /etc/odbc.ini ←この2ファイルを編集する
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /home/ec2-user/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
ODBC設定
まず /etc/odbcinst.ini を開き、末尾に下記を追記する。
[ORACLE_DRIVER]
Description = Oracle ODBC driver
Driver = /opt/oracle/instantclient_23_4/libsqora.so.23.1
次に /etc/odbc.ini を開き、Oracleの接続情報を入力する。
[ORACLE_DNS]
Driver = ORACLE_DRIVER
ServerName = xxx.xxx.xx.xxx:xxxx/orcl
UserName = orclUser
Password = orclPass
接続テスト
isql ORACLE_DNS orclUser orclPass -v コマンドを叩き、OracleにODBC接続できることを確認する。
+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| quit |
| |
+---------------------------------------+
SQL> quit
CREATE TABLEしてみるも上手く行かない
ODBCの設定も上手く行ったようなので、MariaDB上でCREATE TABLEしてみる。
CREATE TABLE TEST_TABLE
ENGINE=CONNECT,
TABLE_TYPE=ODBC,
TABNAME='ORA.TEST_TABLE'
CONNECTION='Driver=ORACLE_DRIVER;DSN=ORACLE_DNS;UID=OrclUser;PWD=OrclPass'
結果はエラー。
#HY000 Unsupported table type ODBC
「TABLE TYPE」で指定できるオプションは公式サイトで紹介されている。勿論「ODBC」も含まれているのに...。
TABLE_TYPE=ODBC が認識されない問題で沼る
ODBC接続できると聞いてCONNECTエンジンをインストールしたのに、肝心の「ODBC」を認識してくれないとはこれ如何に。
"Unsupported table type ODBC"で検索してもごく僅かの情報しかない。
こちらのバグチケット(結構古い)によると、CONNECTエンジンの本体であるha_connect.soのコンパイル時にODBCが有効になっていないとダメらしい。
Solution there was that odbc needs to be compiled in (ODBC_SUPPORT precompiler option).
connect.odbc w5 [ skipped ] No ODBC support
connect.odbc_oracle w5 [ skipped ] No ODBC support
spider/bg.direct_update_part w4 [ pass ] 83
connect.odbc_postgresql w5 [ skipped ] No ODBC support
connect.odbc_sqlite3 w5 [ skipped ] No ODBC support
connect.odbc_sqlite3_grant w5 [ skipped ] No ODBC support
connect.odbc_xls w5 [ skipped ] No ODBC support
ソースを確認すると、TABLE_TYPEを判別した結果がTAB_NIY(定義なし)の場合に"Unsupported table type %s"が出力されることが分かった。
5475 ttp= GetTypeID(topt->type);
~~~~
5532 // Check table type
5533 if (ttp == TAB_UNDEF) {
5534 topt->type = (src) ? "MYSQL" : (tab) ? "PROXY" : "DOS";
5535 ttp = GetTypeID(topt->type);
5536 sprintf(g->Message, "No table_type. Was set to %s", topt->type);
5537 push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
5538 } else if (ttp == TAB_NIY) {
5539 sprintf(g->Message, "Unsupported table type %s", topt->type);
5540 rc = HA_ERR_INTERNAL_ERROR;
5541 goto err;
5542 } // endif ttp
本来なら、5475行目で呼び出しているGetTypeID()関数の中でTAB_ODBCを判定するのだが、
136 #if defined(ODBC_SUPPORT)
137 : (!stricmp(type, "ODBC")) ? TAB_ODBC
138 #endif
ODBC_SUPPORTが有効でない場合に、TAB_ODBC判定部分がコメントアウトされてしまいTAB_NIYと判定されるようだ。
CMakeLists.txtを見る限り、cmake時にCONNECT_WITH_ODBCオプションがONになっていればODBC_SUPPORTは有効になるはずだが...
./storage/connect/CMakeLists.txt
187 OPTION(CONNECT_WITH_ODBC "Compile CONNECT storage engine with ODBC support" ON)
188
189 IF(CONNECT_WITH_ODBC)
190 if(UNIX)
250 IF(ODBC_OK)
251 INCLUDE_DIRECTORIES(${ODBC_INCLUDE_DIR})
252 add_definitions(-DODBC_SUPPORT)
253 SET(CONNECT_SOURCES ${CONNECT_SOURCES} tabodbc.cpp odbconn.cpp)
254 ELSE()
255 SET(ODBC_LIBRARY "")
256 ENDIF()
何らかの理由でCONNECT_WITH_ODBCオプションがOFFでコンパイルされたものが同梱されているのだろうか。
試しにGitHubからソースを持ってきて自分ビでルドしてみたものの、持ってきたソースのバージョンがよくなかったのか、ha_connect.coとMariaDBのバージョンが合わないと怒られてしまった(後述)。
自前でビルドするのは諦める
初心に戻り参考記事のとおり進めることに。
参考記事ではrpmファイルから新しいha_connect.soファイルを抽出していたので、これを真似ることにする。
Amazon Linux 2023はFedoraベースではあるものの、リポジトリはRHELを使うといいとの情報があったので、公式サイトからRHEL用のリポジトリを確認。
rpmファイルをwgetし、rpmファイルから抽出したha_connect.soファイルをMariaDBのpluginフォルダにぶち込む。
wget https://ftp.yz.yamagata-u.ac.jp/pub/dbms/mariadb/yum/10.5/rhel/9Server/x86_64/rpms/MariaDB-connect-engine-10.5.23-1.el9.x86_64.rpm
rpm2cpio ./MariaDB-connect-engine-10.5.23-1.el9.x86_64.rpm | cpio --extract --make-directories
sudo cp -p /home/temp/usr/lib64/mysql/plugin/ha_connect.so /usr/lib64/mariadb/plugin/
sudo chown root:root /usr/lib64/mariadb/plugin/ha_connect.so
あとはMariaDBにログインしてha_connect.soを再インストールする(pluginフォルダに入れただけでは差し替わらないので注意)。
mysql -u user -p
MariaDB [(none)]> UNINSTALL SONAME 'ha_connect';
Query OK, 0 rows affected (0.002 sec)
MariaDB [(none)]> INSTALL SONAME 'ha_connect.so';
Query OK, 0 rows affected (0.010 sec)
ちなみに、バージョンが合わないときはこんな感じで怒られる。
MariaDB [(none)]> INSTALL SONAME 'ha_connect';
ERROR 1126 (HY000): Can't open shared library 'ha_connect.so' (errno: 8, API version for STORAGE ENGINE plugin CONNECT not supported by this version of the server)
ha_connect.soの差し替えが完了したので再度CREATE TABLEを実行するとエラーメッセージが変わった。
#HY000 SQLDriverConnect: [unixODBC][Driver Manager]Can't open lib '/opt/oracle/instantclient_23_4/libsqora.so.23.1' : file not found
無事ODBCは有効になったようだが、次は何だ。
OracleのODBCドライバを認識しない問題
実はこのエラーも参考記事で言及されているのだが、肝心の対応方法が書かれていない。
「mysqlのサービスが動いているLinuxユーザー"mysql"にもOracle ODBC driver (libsqora.so.12.1)へのアクセスが必要」とのことだが、Linuxのmysqlユーザーはnologinユーザーであり、.bash_profileなんて無い。
試しに /etc/environment にも下記を書いてみたが効果なし。
CLIENT_HOME=/opt/oracle/instantclient_23_4 ; export CLIENT_HOME
LD_LIBRARY_PATH=$CLIENT_HOME:$LD_LIBRARY_PATH ; export LD_LIBRARY_PATH
実はこっちの公式Web記事に答えが書いてあった。
sudo vi /etc/ld.so.conf.d/mariadb.conf
で新たなconfファイルを作成し、oracle instant clientのパスを追加。
/opt/oralce/instantclient_23_4/
そしてldconfigを叩くことで変更内容を反映。無事ライブラリパスを通すことができた。
sudo /sbin/ldconfig
ようやくCREATE TABLEが通った
あらためて下記SQLを流すとTEST_TABLEが作成された。
CREATE TABLE TEST_TABLE
ENGINE=CONNECT,
TABLE_TYPE=ODBC,
TABNAME='ORA.TEST_TABLE'
CONNECTION='Driver=ORACLE_DRIVER;DSN=ORACLE_DNS;UID=OrclUser;PWD=OrclPass'
作成されたTEST_TABLEを開くと、OracleにあるORA.TEST_TABLEテーブルの内容が表示された...が、日本語(マルチバイト文字)が「?」に文字化けしている。
文字化けを解消する
Oracle側で下記SQLを実行した結果、Oracle側の文字コードはSJIS(JA16SJISTILDE)であることが分かった。
SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET';
MariaDB側の文字コードがlatin1だったのでutf8に変えてみたが効果なし。
export NLS_LANG=JAPANESE_JAPAN.JA16SJISTILDE
export NLS_LANG=JAPANESE_JAPAN.JA16SJISTILDE
export NLS_CHARACTERSET=JA16SJISTILDE
export NLS_CHARACTERSET=Japanese_Japan.JA16SJISTILDE
など叩いてみるも、現ログイン中ユーザーにしか影響がないため意味なし。
何となくMariaDBというよりはunixODBC→Oracle Instant Clinet→Oracle接続時の文字コード指定が間違っている感じ。
CONNECTエンジンというかunixODBCに「Oracle接続時の文字コードはこれだよ」と指定してやらないといけないのだがやり方が分からない。
しばらくネットの海を漂っていると、公式Webに専用の項目があることに気が付く。
sudo systemctl edit mariadb.service
を叩いて
[Service]
Environment=NLS_LANG=JAPANESE_JAPAN.JA16SJISTILDE
を追記して保存する。最後に
sudo systemctl restart mariadb.service
を叩いてMariaDBのサービスを再起動すると日本語が文字化けしなくなった!これで大体は完了。
未解決問題あり
最後に、いくつか未解決の問題が残っている。
Oracleの日付型カラムを含むテーブルをCONNECTでCREATE TABLEすると、MariaDBではTimestamp型でテーブルが作成されるようだ。そしてそのTimestamp型を含むテーブルをSELELCTするとMariaDBがクラッシュする。
Oracle側のテーブル定義を見てみると型は TIMESTAMP(6) と定義されている。(6)は秒の小数桁数(デフォルト値)。
MariaDB側に作成されたカラムは Timestamp型 で、これは少数桁を持たない。おそらくこのミスマッチが原因だろう。
MariaDBのログを見るとsignal 11ということでバグを踏んでるっぽいのだが判断できず。
[ERROR] mysqld got signal 11 ;
Sorry, we probably made a mistake, and this is a bug.
更に検証してみると、文字列の取得結果に絵文字や「𠮷」などの4バイト文字が入るとクラッシュする。
おそらく特定の型でデータのサイズ長を誤って扱っており、アクセス違反を起こしているのだろう。将来的なバージョンアップで修正されることを期待するしかない。
できる事といえばバリデーションの強化と、Timestamp型を使用しないようCREATE TABLE時に自分で型を指定することぐらいか。
CREATE TABLE時に型を指定する方法
例えば以下のようにCREATE TABLEすると、
CREATE TABLE ORA_TEST_TABLE
ENGINE=CONNECT
TABLE_TYPE=ODBC
TABNAME='ORA.TEST_TABLE'
CONNECTION='DSN=ORACLE_DNS;UID=orclUser;PWD=orclPass';
作成されるテーブルの各カラムの型はCONNECTエンジンの裁量で決められる。
例えばOracle側のORA.TEST_TABLEに日付型の項目があった場合、MariaDB側はTimestamp型で作成されてしまう。
(他の型は大体いい感じに作成されるが、整数値NUMBER(6,0)までもが実直にdecimal(6,0)になるので場合によってはIntegerとかに置き換えてもよさそう)
これを避けるためには、CREATE TABLE時に一つずつ項目名と型を指定する。
CREATE TABLE ORA_TEST_TABLE (
TEST_ID int NOT NULL,
TEST_TIME1 datetime,
TEST_TIME2 datetime,
TEST_TEXT varchar(32)
) ENGINE=CONNECT
TABLE_TYPE=ODBC
TABNAME='ORA.TEST_TABLE'
CONNECTION='DSN=ORACLE_DNS;UID=orclUser;PWD=orclPass';
これでTimestamp型を避けてDatetime型でテーブルを作成することができる。
一つずつ項目を書くのは手間なので、まず項目を指定せずCREATE TABLEし、SHOW CREATE TABLE ORA_TEST_TABLE; を実行して自動生成されたCREATE TABLE文を確認、気に入らない箇所を手直ししてからDROP TABLE→再度CREATE TABLEすると効率がいい。
ちなみに↑の方法でtimestamp型ではなくtimestamp(6)で定義してみたがやはりクラッシュしたので、CONNECTエンジンはOracleのTIMESTAMPデータの扱いに難があるのだろう。
というか、OracleのTIMESTAMP型はタイムゾーンを持たないただの日時情報なんだからMariaDB側はdatetime型で十分なはず。なんでtimestamp型にしてしまうんだろう...。
日付型の制約
他にも問題というかデータ型の制約がある。CONNECTエンジンはこちらに書かれたデータ型にしか対応しておらず、MariaDB(InnoDBエンジン)が対応しているデータ型よりかなり制限される。
特に厳しいのが日付型。内部的には4バイトの符号付きIntegerで、0=1970/1/1 12:00:00 amを起点に 1901/12/13 20:45:52 ~ 2038/1/18 19:14:07のデータしか保持できない。
CONNECT handles dates from 13 December 1901, 20:45:52 to 18 January 2038, 19:14:07.
CONNECT Data Types - MariaDB Knowledge Base
もしリンク先のテーブルがこれを超える値だった場合、SELECTで取得される値はオーバフロー/アンダーフローした結果となる。
例えば元のデータが 3999/12/31 00:00:00 とすると、このデータをCONNECTエンジンで取得すると 1970/1/1 9:00:00 となる。
これは結構厄介で、気づかないうちに意図しない日付になってしまう可能性がある。
対策としては、リンク先のDBで(ビューを作るなどして)日付を文字列としてVARCHAR型で保持、CONNECT後にDATE_FORMAT関数などを使って日付型に戻す方法が紹介されている。
もしくは、リンク先のビューで大きすぎる値は最大値(2038/1/18 19:14:07)に、小さすぎる値は最小値(1901/12/13 20:45:52)に置き換えてSELECTするようにしておけばオーバフロー/アンダーフローしなくなる。
その他のTips
CREATE TABLE時、TABNAME='ORA.TEST_TABLE'で指定したテーブルに存在しない項目を指定するとSELECT時にエラーになる。
ORA-00904: "AAAAA": invalid identifier
↑適当に「AAAAA」という項目を指定してCREATE TABLEし、SELECTしてみた結果。
CREATE TABLE時はエラーの有無をチェックせず処理成功してしまうので、テーブル作成後に一度SELECTしてエラーが出ないかどうかの確認は必須。
ということで、いくつか問題はあるものの無事接続はできた。
他にも実用上の問題や制約が出てきそうではあるが、一応使えるようになったので一旦はこれで運用してみる。
コメントする