ちょっと硬派なコンピュータフリークのBlogです。

カスタム検索

2009-02-25

DB移行の7つのステップ

データベース移行ではどのような作業を行わないといけないのか?全体的な流れを俯瞰的に見なければ、作業の見積もりなどが難しいことだろう。というわけで、今日はデータベース移行時に必要になる作業について7つのステップに分けて紹介しようと思う。言うまでもないことであるが、移行時には既存システムを直接いじるわけではない。(稼働中のシステムを直接いじるのは、走ってるクルマに乗り込むのと同じぐらい危険な行為である!!)開発用のシステムを別途用意して作業を進めるという前提で読んで欲しい。

1. データベース構築

まずは何はともあれ新規データベースを構築することである。MySQLであれば典型的な構成にしておけばそれなりに高速に動作するので、細かいチューニングは後で問題が出てからやれば良い。my.cnfはインストールディレクトリの下のsupport-filesディレクトリにあるものを弄ってもいいし、MySQL Practice Wikiのサンプルをカスタマイズしてもいい。多くの場合はdatadirやinnodb_buffer_pool_sizeを調整するだけである。

データベースの構築が済んだらユーザの作成を行おう。OracleとMySQLではMySQLにはRoleというものは存在しないなど仕組みが異なるので注意しよう。

2. 接続設定の変更

Oracleを利用しているなら最も多く利用されている開発言語はJavaではないだろうか。Javaの場合、まずは新規データベース用のJDBCドライバを入手してCLASSPATHに追加する。MySQLの場合ならJDBCドライバはConnector/Jである。現時点で最新版の5.1.7をダウンロードして利用するといいだろう。
http://dev.mysql.com/downloads/connector/j/5.1.html

また、JDBCを直接利用している場合には接続URLやプロパティの変更を、アプリケーションサーバ利用時にはDataSourceの変更などを行う。例えばMySQLではJDBCの接続は次のように行う。

Class.forName("com.mysql.jdbc.Driver");
Properties props = new Properties();
props.setProperty("user", "scott");
props.setProperty("password", "tiger");
props.setProperty("useCursorFetch", "true");
props.setProperty("defaultFetchSize", "100");
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/db", props);

3. データ移行

最初の難関である。

データ移行にはテーブル定義とデータのコピーという2つのフェーズがある。データのコピーはそれほど難しくないのだが、テーブル定義および各種オブジェクトの定義には手が掛かる場合が多い。何故ならば、Oracleにしかないデータ型やオブジェクトが存在するからである。例えば、
  • ROWID型
  • NUMBER型
  • シーケンス
  • シノニム
  • ビットマップインデックス
  • マテリアライズドビュー
など。

対応するデータ型やオブジェクトが存在しない場合には別途実装方法を考える必要がある。例えばシーケンスは次のようなテーブルで代用するなど。シノニムは使えなければ元のテーブル名をそのまま利用すれば良い。

CREATE TABLE seq1 (seq BIGINT SERIAL);

データのコピーは移行元のデータベース(Oracleなど)からCSVなどにはき出して、移行先のデータベース(MySQLなど)でインポートするなどの方法がある。別にCSVでなくても構わないのでお好きな方法でどうぞ!!

MySQL Migration Toolkitというツールを利用すると、データ型の対応付けやデータのコピーをある程度自動的に行ってくれる。(完全に自動でできるツールなどはこの世に存在しないだろう。)Migration ToolkitはJDBCドライバを利用することで、稼働中のOracleに接続してテーブル定義の変換やデータをコピーすることができるツールである。Oracle以外にも移行元データベースとしてMS SQL Server、AccessおよびMySQL自身をサポートしている。完全に自動化するのは難しいが、移行作業をある程度自動化してくれるのでトータルの作業量はかなり減ることになるだろう。最後に作業レポートを出力してくれるので、どのようなデータ型変換を行ったかなどが後からレビューすることができるようになっている。

Migration ToolkitはMySQL GUI Toolsに含まれている。次のページから入手しよう。

MySQL GUI Tools
http://dev.mysql.com/downloads/gui-tools/5.0.html

データのコピーは最終的にサービスを切り替える時に改めて行う必要がある。(更新しないならば話は別であるが、データベースを更新しないサービスなどこの世に存在しないだろう。)ここでコピーしたデータは開発時のサンプルとして利用するといいだろう。

4. SQL文の書き換え

厄介な問題なのだが、データベースによってSQL文の方言が存在する。特にGRANTの構文、JOIN、演算子の優先順位などにはデータベースごとに若干の異なるので注意が必要だ。なのでアプリケーションにおいてSQLを直接記述している場合には、SQL文を一つずつレビューする必要があるだろう。例えば、演算子の優先順位などが異なる場合にはSyntaxは合っていても期待通りの結果が返ってこないという事態になり得るという具合である。(やっかいでしょ?!)OracleとMySQLでは以下のような優先順位の違いがある。

Oracleの場合
  1. 算術演算子 (! + - * / << >> など)
  2. 比較演算子 (= <> < <= > >=)
  3. IS [NOT] NULL, LIKE, [NOT] BETWEEN, [NOT] IN, EXISTS, IS OF type
  4. NOT
  5. AND
  6. OR
MySQLの場合
  1. 算術演算子 (! + - * / << >> など), NOT
  2. 比較演算子 (= <> < <= > >=), IS [NOT] NULL, LIKE, [NOT] IN, REGEXP
  3. [NOT] BETWEEN
  4. AND
  5. OR
また、SQL文を実行したときにはエラー処理についても考えなければいけないが、各データベースが返すエラー番号はデータベースごとに全く違う。エラー番号に応じた細やかなエラー処理を行っている場合には、エラーの対応付けなどを行う必要があるだろう。

MySQLのエラーメッセージは次のページにまとめられている。
Server側: http://dev.mysql.com/doc/refman/5.1/ja/error-messages-server.html
Client側: http://dev.mysql.com/doc/refman/5.1/ja/error-messages-client.html

5. ストアドプロシージャや解析関数の移行

ここが2番目の難所である。

そもそも、ストアドプロシージャやトリガの文法はデータベースによって大きく異なる。Oracleは独自のPL/SQLによって記述するが、MySQLの場合はANSI準拠であり、その文法はかなり異なる。(MySQLにおけるストアドプロシージャ実装はDB2に近い。)ストアドプロシージャをたくさん利用していればしているほど、それだけ移行作業は困難になってしまう。また、一部の解析関数、例えばRANK()などはMySQLには存在しないので、そのような関数の移行も行う必要がある。

そのような差異をどのように吸収すればいいのだろうか。方法は色々あるだろうが、主なのは次の通り。
  • ストアドプロシージャを完全に書き換える。(対応するストアドプロシージャを作成する。)
  • ストアドプロシージャの利用を止めて、Javaなど上位のアプリケーションにおいて機能を実装する。
  • 一部の解析関数の代わりにストアド関数を作成する。
  • 一部の解析関数の代わりにSQL文を工夫する。
  • MySQLを改造してネイティブ関数を実装する。
個人的には2番目の方法がオススメである。後々データベースをさらに別のものに移行する場合に移行の手間を省けるからである。また、データベースの移行に伴いアプリケーションそのものを移行することも多々あるだろう。そのような場合には、ORMを利用してデータベースへのアクセス方法を全く異なるロジックで行うなどの方法が考えられる。

ちなみに、上記のRANK()は次のSQL文で代用可能である。

mysql> set @x=0, @y=1, @last=-2147483648;
mysql> select col1, @x := @x + 1 AS rownum,
    -> @y:=IF(@last = col1, @y, @x) AS rank,
    -> @last:=col1 AS dummy FROM table;

6. 監視やバックアップなどの運用設計の見直し

データベースが違えば監視やバックアップなどの運用周りも作り直す必要がある。

監視についてはMySQL Enterprise Monitorという商用のツールが便利である。MySQL Enterprise Monitor単体で監視コンソールとして利用することも可能であるが、SMNP経由で他の監視ツール(Tivoli、Openview、JP1、Nagiosなど)と連携することが可能である。

バックアップに関しては下記に示す各種フルバックアップとバイナリログによる差分バックアップを併用することが多い。フルバックアップには種々の方法があるので、要件に応じて使い分けるといいだろう。
  • mysqldump
  • 商用のInnoDB Hot Backupツール
  • 商用のZmanda Recovery Manager
  • コールドバックアップ(停止時にデータをそのままコピー)
  • LVMやZFS、ストレージ装置におけるスナップショット
また、コールドバックアップを採取する場合、MySQLレプリケーションを利用してスレーブから行う方法がオススメである。そうすることでマスター上で一切バックアップの負荷が発生しなくなるからである。

7. QAテスト

さて、最後の山場である!!

全ての移行および構築が完了したなら、仕様通りにアプリケーションが動作するかどうかをテストする、いわゆるQA(Quality Assurance)テストを行う。QAテストをしっかりやっておかないと、本番環境に変更を加えた後で泣きを見ることになるだろう。既に自動化されたテストケースを持っている優等生であればこのフェーズはそれほど大変ではないが、テストケースが定義されていない場合にはテストケースを洗い出すところから始めなくてはならないので大変である。

テストケースが失敗したらアプリケーションのデバッグを行うわけであるが、もしも問題がデータベースに起因するものであれば、MySQLの一般クエリログを利用すると便利である。一般クエリログはMySQLサーバへ送信された全てのSQL文を記録するためのログである。一般クエリログはすぐに肥大化するし、ログを記録するためのオーバーヘッドも馬鹿にならないので本番環境で利用することは殆どない。しかしアプリケーションのデバッグ時には非常に強力なツールとなる。一般クエリログの使い方などについてはマニュアルを参照して欲しい。

MySQL 5.1からは一般クエリログのON/OFFを動的に切り替えられるようになった。次のような手順でテストを行うといいだろう。
1) 一般クエリログを有効にする

mysql> SET GLOBAL general_log=ON;

2) データベースへ再接続を行う。
3) テストケースを実行する。
4) 一般クエリログを無効にする。

mysql> SET GLOBAL general_log=OFF;

5) データベースから切断する。

こうすることで、特定のテストケース実行中だけのクエリログを採取することができる。

アプリケーションは仕様通りに動くがパフォーマンスがよくない!ということが発覚するのもこのフェーズである。どの問題のある遅いクエリを見付けるには、MySQLの機能であるスロークエリログを利用するといい。スロークエリログは長時間かかるクエリや、インデックスを使用しないクエリを記録するためのログである。スロークエリログに関しては以前の投稿で少し解説したのでそちらを参照して欲しい。また、商用のMySQL Enterprise Monitorに付属しているクエリアナライザを利用すると、より簡単に問題のあるクエリを発見することが可能である。

また、最終的には本番環境を入れ替えることになるわけだが、移行時作業の手順もしっかり作り込んで、事前にテストを行っておく必要がある。移行手順はスクリプトなどで自動化しておくとミスを減らすことができるし移行時間の短縮にもなるのでオススメである。

まとめ。

今日はデータベース移行時にどのような作業が必要になるかということについて説明した。もちろん実際にはもっと細かい調整が出てくることになるだろう。作業の流れはだいたいこのような感じであるので、これまで「データベースの移行なんてどうやっていいか分からないよ!」と思っていた人の道しるべになれば幸いである。また、大ざっぱにどの程度の作業量(つまりコスト)が発生するかということを見積もる際に参考にして欲しい。

Let's challenge!!

0 コメント:

コメントを投稿